MySQL 分库分表实践
这些日子为了解决业务上的挑战,想要解决 MySQL 的性能提升方案。
目前找了主要有:
- 分库分表
- 读写分离
读写分离最简单,牺牲一点一致性能减少读的压力,分表也比较简单,但是目前没有特别合适的中间件,结合我们的业务场景,分库是目前最适合下点功夫的。(另外还需要做高可用,可以做双主,但是不需要我过多参与,因此这里不谈。)
背景介绍
- MongoDB 用来存储大部分非事务相关的业务
- MySQL 用来做电商相关业务,所有数据几乎都与账户 ID 相关
- 账户 ID 是 MongoDB 的 ObjectId
- 对于普通用户来说没有关联到其他用户的操作
- 管理员需要统计以及查看所有的用户数据
分库算法
一致性 hash
需要停机,遍历所有库,将需要移动的数据移动到新的库上。【1】【3】
具体步骤
停机 => 数据迁移(不删除旧数据) => 调整服务器配置 => 上线 => 遍历检查并删除各个库冗余的数据
优点
- 平滑提升性能,节约服务器
- 迁移过程中移动数据很少
缺点
- 线上迁移时间时间会比较久
- 计算速度没有取模快
取模
- 也算是一种一致性 hash,只不过更简单;
- 拆库的方案是按 2 的倍数去拆,比如先拆成 2 个库,然后需要扩容的时候拆成 4 个,不然的话可能每次迁移都得全部重新迁移;
具体步骤
停机 => 所有的库复制一份 => 调整服务器配置 => 上线 => 遍历检查并删除各个库冗余的数据
优点
- 数据会非常均匀;
- 由于不用操作迁移数据库,直接使用备份恢复的方式,线上扩容速度会快很多;
- 一次拆库之后能坚持很长时间;
缺点
- 比较耗服务器,比如每个库最多放 100W 个用户的数据,那数据涨到 150W 左右的时候就应该拆分了;
无论怎么拆,性能都是跳跃性提升的,不能平滑提升; - 由于我们使用的是 mongodb 的 ObjectId,数据不均匀的可能性非常高;
好了,无论怎么分,都会涉及到停机升级的问题,但是停机意味着损失收入,那么为了解决停机的问题,我们找到了一个迁移方案:** 索引表 **。【2】
经过上面的比较,为了让数据库不停机以及迁移尽可能快,我们选择 一致性 hash 方案再加上索引表
:
- 每次根据用户 ID 取库的时候,先查索引表,如果没有,再计算;
- 开启定时任务,定期扫描,迁移数据,再更新索引表;
迁移需要注意一点就是需要锁住数据,手动进行 两阶段事务
,不然可能会造成数据不一致,然后这个迁移任务尽量在凌晨无人的时候进行,而且也只是单个用户,因此这样可以将对用户的影响减到最少。【2】
几个问题
- 是否需要公共库?
如果是公共库,很可能是跟事务不相关的,移到 MongoDB 即可。- MySQL ID 自增怎么解决?
换成 ObjectID,或者 UUID 之类的。- Node.js 有没有相关的中间件?
可以看看 TribeDB,但是不满足我们目前的需求。(蠢蠢欲动想要造轮子了- select *,order by 之类的需要跨库,或者联合所有库的操作怎么办?
这些操作在我们这一般是管理后台才要用,所以可以放到其它工具去做,比如 elasticsearch,而且这样的查询会影响数据库性能,我们巴不得砍掉这些查询
其他问题可以看看【2】,内容很详实。
Reference
【1】. https://cnodejs.org/topic/5502a23573263b0e4eef9b85
【2】. http://www.infoq.com/cn/articles/yupoo-partition-database
【3】. https://github.com/3rd-Eden/node-hashring
原链接: https://github.com/xizhibei/blog/issues/20
本文采用 署名 - 非商业性使用 - 相同方式共享(BY-NC-SA) 进行许可。