背景
部分业务上MySQL的使用去到了极限, 单表数据量逼近1TB, 需要评估其他的解决办法.
为什么说MySQL是单机KV服务
KV服务的定义:
- 键值访问
- 键值范围访问
(大数据量下) MySQL的局限性
- 宽表设计模式下, 有效值稀疏, 浪费存储
- DDL变更基本是触发重建表, 大表业务加字段需求比较困难, 会比较重
- 随机写入存在瓶颈 (根本原因 B-Tree vs LSM)
- 因此才会有MyRocks之类的换存储引擎的方案
- 二级索引回表开销较高 (数据页内存命中低导致频繁换页)
- 复杂语句执行, 如聚合/排序等, 会导致严重的事务问题
- 因此:
因此, 数据量大之后, MySQL表只能当作一个支持SQL语法访问的单机KV来用, 慎用二级索引, 当然要完全避免扫表筛选逻辑.
对于高TPS的MySQL表, 查询必须限制在SELECT ... WHERE primary_key = ...
模式下.
拍脑袋的经验: 单表(单分区)数据量超过100GB, 或超过1kw行后, 需要特别注意读写语句是否足够简单.
其他可以认为是单机KV的服务/存储:
- LevelDB
- RocksDB
- Redis
- SSDB (Redis over LevelDB)
- …
分布式MySQL
本质上基于主键表分实例, 对于中间件及变更运维质量依赖要求高. 最重要的是, 资源需要提前预留好, 不能按需扩容.
基于MySQL的改进云服务数据库
通过改写IO相关实现, 以及后面挂分布式存储, 来优化读写性能的问题:
产品思路是面向客户的MySQL通讯协议不变, 从而最简化上车成本, 后面具体内部实现逻辑再慢慢换掉.
分布式KV服务
或者说NoSQL数据存储服务, 以显得时髦/特别.
- Google的Bigtable论文, 一切的缘起
- GCP上已经提供云服务: Cloud Bigtable
- HBase: 依赖HDFS的Bigtable开源实现
- AWS有牛逼闪闪的Dynamo论文撑腰的DynamoDB服务
- TableStore/OTS/表格存储: 依赖于阿里云”盘古”分布式存储系统的, 可以简单理解为阿里云上HBase服务
- 说明: Bigtable及OTS均支持通过HBase API访问, 可见HBase在分布式KV存储中的地位, 但是具体功能上会有些区别/限制:
OTS vs MySQL
- 不需要预先定义好列, 可以随便加字段
- 基于时间戳的版本控制和数据淘汰
- 支持自增ID相关功能
- OTS有通道服务, 并且按照分区键并行化, 类似MySQL的binlog机制, 便于我们做数据变更触发同步逻辑
OTS使用限制
- 主键列不能超过四个
- 分区字段, 由于不可再拆分建议控制大小不超过10GB
- 单实例表不能超过64个 (可工单加)
- 字符串字段类型大小不能超过1KB
- …
成本相关
按需计费的服务有好处, 也有坑点, 使用上一不小心容易产生天价账单. 所以要格外注意成本测算相关, 并有相关成本告警.
成本对比
MySQL:
- 自建ECS: 固定开销
- 存储 (高效云盘算): 2.3/100GB/天 (说明: 含从库计算)
OTS: 按需计费 (按量容量型算)
- 读: 4元/千万计费单元
- 写: 2元/千万计费单元
- 存储: 1元/100GB/天
总的来说, 存储成本比自建MySQL要低 (自然低过RDS), 更适合存文本类型的属性字段; 需要排序筛选的字段, 一般数值型, 或者枚举的, 相对而言表大小可控, 可以MySQL里面做相关排序筛选逻辑.
参考
成本优化策略
写入成本优化思路: 降频写入, 离线脚本定期写入数据, 同现在kafka2mysql离线化的逻辑. 在数据同步实时性和写入量之间找折衷.
查询成本优化: 评估直接查询性能是否满足要求, 查询成本, 以及根据查询单独建索引表的成本
其他优化手段: 买预留
二级索引
不同于MySQL的二级索引指向原表数据行, OTS的二级索引本质上是自动另外维护另外一个表, 单独计费.
区别
- 全局二级索引
- 多元索引 -> 倒排索引, 支持简单的文本搜索, 临近搜索类需求
DLA映射表
支持DLA上创建OTS的映射表, 可以和其他数据源做JOIN查询, 写SQL直接读写OTS上的数据
一些使用备忘:
- 不能自动推导并利用相关索引表, 不能针对索引表做映射
- 从实践来看, 只支持varchar/bigint类型, 和官方文档不符
- 测试个别查询/大范围扫描会遇到假死问题, 待进一步研究
OTS vs DDB 主观使用感受区别
- OTS强事务支持, 比垃圾DDB的”最终一致性”要好
- OTS读写成本比=2:1, DDB读写成本比=1:5, 猜测从目标使用场景上, DDB还是优化写的, OTS是优化读质量的, 所以OTS做强事务支持也不是没有成本的
- 写入性能非科学性能对比结果:
- DDB (海外广告点击会话归因业务) 200QPS/instance (aws-golang-sdk)
- OTS (国内广告采集业务) 2500QPS/instance (ots-python-sdk)
- 猜测: OTS传输协议用的proto, DDB是JSON, 读写消息序列化性能及网络带宽开销更优
- …
附: MySQL里面做”万金油”表
由于业务上各种字段需求比较多, 大表又加不动字段, 因此MySQL表设计的一种”万金油”做法:
create table xx_kv (
id int primary key comment '主键',
k tinyint comment '列名',
v varchar(1024) comment '值',
key (k, v(16))
);
可以达到类似的节约宽表结构设计导致的空间浪费, 以及不方便动态加字段的弊端, 甚至还可以对任意字段查询走索引. 也可以针对数值字段/字符字段分两张表处理, 从而支持数值范围查询.
弊端在于不方便做多字段筛选, 以及需要基于筛选条件拿到对象全部属性值时需要分两步走.