场景
公司需要做一款替用户记录信息的 App, 随着用户量增加, MySQL 单表数量达到 2000 万. 写请求频繁, 响应变慢. 读请求响应也变慢. 于是决定分库分表.
解决思路
1. 数据得从一个表分到多个表.
2. 分到多个表后, 能否对代码改动较少就可以支持查多个表.
3. 怎么不停服务迁移.
这个第 2 点其实有很多中间件是可以支持的, 比如我采用 mycat. 原理是走代理模式, 我对物理表操作的请求会先到 mycat, 再由 mycat 去对应的表操作并返回. 比如用户信息表由 user 变成了 20 个子表, user1,user2...user20. 指定了按用户 id hash 路由, 这个时候如果我插入一条 user 数据, 我不用修改代码, 配置数据源为 mycat,mycat 会帮我根据用户 id 插入到对应的子表.
那这个时候可以来重点考虑下迁移数据的问题.
首先如果要保证不停服务的话, 必须要区分的就是旧数据, 和一直在增加或修改的新数据. 怎么才能一边迁移旧数据, 一边保证正在迁移的数据不会因为修改而丢失且新增的数据不会漏调迁移呢?
于是有如下这种方案:
1. 在原有操作旧表的基础上, 将涉及修改操作的 sql 通过消息队列或其它方式存起来.
2. 开始通过脚本迁移旧表数据到新表, 从第一条到消息队列保存的第一条 insert id 的前一条.(同步旧数据)
3. 旧数据迁移完成之后, 新库开始消费队列消息.(同步增量数据)
4. 消费的差不多之后, 修改旧数据库链接指向新库.
怕有问题怎么办?
数据对比, 同样写个脚本, 对比两边库的数据就行, 可以都分批次取数据, 做签名, 要是有问题, 再二分法取数据签名, 找出有问题的数据, 记录. 其实正常情况也是可能有不一样的, 比如在修改到新库后, 用户有对旧数据的修改, 可以先把脚本写好, 尽量早点执行对比.
这种方法有没有问题?
想了下, 一般的没啥问题, 但如果有一些修改的 sql, 类似这种 update a=a+1; 这种如果在旧库数据同步到新库前执行过, 同步到新库后, 回放增量消息就会又执行一遍, 所以得注意下.
完成
这个时候数据也导入到新表了, 原来的单表操作的 sql 也不用修改, 都由 mycat 代理帮忙完成了, 那有没有遇到什么坑呢?
其实是有的, 最坑的是发现批量插入不能用了, 为啥呢, 因为一批数据可能路由到不同的表, 当时 mycat 是不支持的, 那怎么解决呢, 我将批量的数据先按 mycat 的路由规则分好类, 插入的时候直接指定批量到对应的物理表. 暂时解决了.
补充
其实中间也是有一些小插曲的,
比如突然想给新表修改下联合索引.
MySQL 当时版本修改索引是会锁表的, 我又建了新表, 再同步旧数据到新表, 可以用 mysqldump 在 MySQL 本机导出数据, 再直接导入新表, 减少网络传输耗时, 这个时候是同一台机器嘛. 因为比较快, 这里就直接停服操作了, 其实想想也是可以按上面的思路来的, 总的来说就是一个同步旧数据和同步增量数据的问题
来源: http://www.jianshu.com/p/65357593903c