这些日子为了解决业务上的挑战,想要解决 MySQL 的性能提升方案。

目前找了主要有:

  1. 分库分表
  2. 读写分离

读写分离最简单,牺牲一点一致性能减少读的压力,分表也比较简单,但是目前没有特别合适的中间件,结合我们的业务场景,分库是目前最适合下点功夫的。(另外还需要做高可用,可以做双主,但是不需要我过多参与,因此这里不谈。)

背景介绍

  • MongoDB 用来存储大部分非事务相关的业务
  • MySQL 用来做电商相关业务,所有数据几乎都与账户 ID 相关
  • 账户 ID 是 MongoDB 的 ObjectId
  • 对于普通用户来说没有关联到其他用户的操作
  • 管理员需要统计以及查看所有的用户数据

分库算法

一致性 hash

需要停机,遍历所有库,将需要移动的数据移动到新的库上。【1】【3】

具体步骤

停机 => 数据迁移(不删除旧数据) => 调整服务器配置 => 上线 => 遍历检查并删除各个库冗余的数据

优点
  • 平滑提升性能,节约服务器
  • 迁移过程中移动数据很少
缺点
  • 线上迁移时间时间会比较久
  • 计算速度没有取模快

取模

  • 也算是一种一致性 hash,只不过更简单;
  • 拆库的方案是按 2 的倍数去拆,比如先拆成 2 个库,然后需要扩容的时候拆成 4 个,不然的话可能每次迁移都得全部重新迁移;
具体步骤

停机 => 所有的库复制一份 => 调整服务器配置 => 上线 => 遍历检查并删除各个库冗余的数据

优点
  • 数据会非常均匀;
  • 由于不用操作迁移数据库,直接使用备份恢复的方式,线上扩容速度会快很多;
  • 一次拆库之后能坚持很长时间;
缺点
  • 比较耗服务器,比如每个库最多放 100W 个用户的数据,那数据涨到 150W 左右的时候就应该拆分了;
    无论怎么拆,性能都是跳跃性提升的,不能平滑提升;
  • 由于我们使用的是 mongodb 的 ObjectId,数据不均匀的可能性非常高;

好了,无论怎么分,都会涉及到停机升级的问题,但是停机意味着损失收入,那么为了解决停机的问题,我们找到了一个迁移方案:** 索引表 **。【2】

经过上面的比较,为了让数据库不停机以及迁移尽可能快,我们选择 一致性 hash 方案再加上索引表

  • 每次根据用户 ID 取库的时候,先查索引表,如果没有,再计算;
  • 开启定时任务,定期扫描,迁移数据,再更新索引表;

迁移需要注意一点就是需要锁住数据,手动进行 两阶段事务,不然可能会造成数据不一致,然后这个迁移任务尽量在凌晨无人的时候进行,而且也只是单个用户,因此这样可以将对用户的影响减到最少。【2】

几个问题

  1. 是否需要公共库?
    如果是公共库,很可能是跟事务不相关的,移到 MongoDB 即可。
  2. MySQL ID 自增怎么解决?
    换成 ObjectID,或者 UUID 之类的。
  3. Node.js 有没有相关的中间件?
    可以看看 TribeDB,但是不满足我们目前的需求。(蠢蠢欲动想要造轮子了
  4. 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) 进行许可。