为什么要分库分表,分库分表的实践请看之前的文章:微服务-分库分表思路
分库分表之前我们首先要选出分片字段、然后根据一定的分片算法将数据写入不同的库表,查询数据的时候根据分片字段+分片算法从对应的库表中拿取数据即可
因此,由单独进行横向拆分后需要将老数据按照我们定义的,分片字段和分片算法重新洗数据,那么我们本节主要讲解3中方法:停机数据迁移、新老库双写、写老库通过binlog同步新库
一、上线前停服数据迁移
实践思路:
- 新建线上新库(按照分片键+分片算法新建)
- 编写数据同步程序(查询老库,然后按照分片算法迁移到新库)
- 在数据访问最少的时间段(比如半夜2点)停服务、开始跑我们的同步程序
- 数据同步完成后(要进行数据一致性校验,比如校验总数)上新代码,连接新库
优点:
- 数据一致性能得到保障
- 简单,没什么技术含量
缺点:
- 需要停服务(如果不停服务可能导致已经同步的数据在同步过程中有变更,导致数据不一致)
- 数据同步时间较长,出错率高
- 熬夜
题外话:最后一步一定要做数据校验,如果停服务的话一般校验数据总条数即可,如果同步过程中出现错误,一定要记录,否则数据矫正略麻烦;如果真的出现数据不一致的情况,提供一直校验思路:
同样的查询条件分表查询新老库,分别MD5,然后做对比
二、新老库双写
上一种为什么要停服务:就是怕在迁移过程中,有数据修改出现数据不一致的情况,那么为了解决数据不一致的情况我们可以在迁移过程中写的数据,同时写入新老库

流程解释:
- 新建线上新库(按照分片键+分片算法新建)
- 修改源码,将之前代码新增、修改、删除的地方都修改成同时写新老库
- 写入数据库中间件之前先判断新库中是否有此次操作的数据
- 新增操作:如果新库无数据:直接新增,如果有数据:直接丢弃
- 修改操作:如果新库有数据:确保新数据覆盖旧数据,如果无数据:直接丢弃
- 删除操作:如果新库有数据:直接删除,如果无数据:直接丢弃
- 启动后台数据迁移程序
- 查询老库,查出数据后通过新库规则查询新库,如果无数据直接写入,如果有数据比较时间戳,确保新数据覆盖旧数据,如果是老数据想覆盖新数据,直接丢弃
- 数据迁移完毕后,进行数据校验
- 修改源码,将之前同时写新老库的地方全部改成只写新库
- 经过一段时间,无异常后下掉老库和迁移工具
优点:
- 对用户而言无感知,不需要停服务
- 有充足的时间检验数据的准确性(切新库之前所有用户操作都走的老库)
- 不用熬夜
缺点:
- 代码侵入,需要修改两次源码,容易漏
总结:上种方法需要很多的判断,判断新库中是否有数据,如果有怎么处理,如果没有怎么处理,牵扯到了一些逻辑,因此我们有没有一种办法不用判断
第二种优化策略

解释:
- 新建线上新库(按照分片键+分片算法新建)
- 启动后台数据迁移程序,只迁移指定时间以后的历史数据
- 修改源码,将之前代码新增、修改、删除的地方都修改成同时写老库可放入消息队列
- 历史数据迁移完毕校验通过后,迁移程序开始监听消息队列,将挤压消息写入到新库
- 当新老库数据校验通过,切到读写新库
总体思路:先迁移老数据,然后消费新数据,不需要各种逻辑判断
当然缺点还是没有避免,还是要修改源码,有没有不修改源码的呢,,,
三、读写老库,通过老库binlog同步新库

解释:
- 将老库binlog开启row level模式下
- 新建线上新库(按照分片键+分片算法新建)
- 启动后台数据迁移程序,只迁移指定时间以后的历史数据
- 历史数据迁移完毕校验通过后,迁移程序开始监听binlog的增删改事件,将消息写入到新库
- 当新老库数据校验通过,切到读写新库
题外话:监听binlog增删改事件可以用阿里的canal
公众号主要记录各种源码、面试题、微服务技术栈,帮忙关注一波,非常感谢

|