迁移是最容易出故障的一个点。
那么如何做数据迁移呢?
1 解决方案
1.1 全量
最直观的一把梭方案,即全量数据的导入/出:
- 业务系统需要停机
- DB 迁移,校验一致性(数据、关系、约束等)
- 升级业务系统,接入新 DB
如果直接复制,可以 dump 后全量导入,如果是异构数据,需要用程序处理。
优点:简单
缺点:停机时间过长,数据量不太大时适合这种方案
1.2 全量+增量
大部分开发采用的方案,依赖数据本身的时间戳,即版本号:
- 先同步数据到最近的某时间戳
- 然后在发布升级时停机维护
- 再同步最后一段事件(通常是一天)的变化数据
- 最后升级业务系统,接入新数据库
优点:极大缩短停机时间
看来已经满足绝大部分需求了,还有更流弊的方案吗?
1.3 binlog+全量+增量(推荐)
当你的公司数据库和中间件比较完善时,推荐使用。
通过主库或从库的binlog解析和重新构造数据,利用主从复制实现扩展迁移,这需要中间件的支持。可实现多线程,断点续传,全量历史和增量数据同步。
可以达到:
- 实现自定义复杂异构数据结构
- 实现自动扩容和缩容,比如分库分表到单库单表,单库单表到分库分表,分4个库表到分64个库表
当然了,既然这种需求很常见,社区肯定也有支持的框架:
1.4 shardingSphere-scaling
- 支持数据全量和增量同步
- 支持断点续传和多线程数据同步
- 支持数据库异构复制和动态扩容
- UI界面,可视化配置
MySQL不像MongoDB支持数据Auto Sharding(自动分片),当:
- MySQL单库拆成多库
- 还是由于数据存储的瓶颈,不得不将多库拆成更多库
你都要考虑如何数据迁移。不只是对DB拆分时会做数据迁移,很多场景都要给出数据迁移方案。
比如领导突然想将应用从自建机房上云,你要考虑将所有自建机房中的数据,包括MySQL、Redis、MQ等组件中的数据全部上云!
如何平滑迁移DB数据
数据迁移无非是将数据从一个DB拷到另一个DB,可通过:
- MySQL 主从同步做到准实时数据拷贝
- mysqldump工具将源库的数据导出再导入到新库
这有啥复杂的呢?注意了!这两种方式只支持:单库=》单库,不支持单库=》多库多表。
即便是单库=》单库,迁移过程也需满足:
- 迁移应该是在线的迁移,也就是在迁移的同时还会有数据的写入
数据应该保证完整性,也就是说在迁移之后需要保证新的库和旧的库的数据是一致的 - 迁移过程要做到可回滚,一旦迁移过程异常,可立刻回滚到源库,不影响系统可用性
若使用Binlog同步,在同步完成后再修改代码,将主库修改为新库,这就不满足可回滚!
一旦迁移后异常,由于已经有增量数据写入新库,未写入旧库,不可能再将DB改成旧库了。
数据库迁移方案
双写
步骤
将新库配置为源库的从库用来同步数据;若需要将数据同步到多库多表,可使用第三方工具获取Binlog的增量日志(如Canal),获取后即可按分库分表写入到新库表
1、2 之间切换为双写前是不是应该停掉新旧库的同步关系。主从延迟是可以监控的,可以看主从没有延迟了就可以断掉同步了。
同时需改造业务代码,在数据写入时,旧库新库都要写。考虑到性能,可异步写新库,只要保证旧库写成功即可。但注意,要将写新库失败的数据记录在单独日志,方便后续补数据,保证新、旧库数据一致性
然后开始校验数据
由于DB数据量很大,全量数据校验不现实。可抽取部分数据,具体数据量依总体数据量而定,只要保证这些数据一致即可。推荐你在未开始迁移数据前,先写好数据校验工具或脚本,在测试环境上测试后,再正式迁移。
若一切顺利,即可将读流量切到新
采用灰度切换,比如开始切换10%流量,没有问题再切50%流量,最后再切到100%。
由于双写,所以在切换过程中出现任何的问题,都可将读写流量随时切到旧库,保障系统性能
观察几天,发现数据迁移没问题后,即可将DB双写改造成只写新库,数据迁移就完成了。
- 将新库配置为源库的从库用来同步数据;若需要将数据同步到多库多表,可使用第三方工具获取Binlog的增量日志(如Canal),获取后即可按分库分表写入到新库表
1、2 之间切换为双写前是不是应该停掉新旧库的同步关系。主从延迟是可以监控的,可以看主从没有延迟了就可以断掉同步了。 - 同时需改造业务代码,在数据写入时,旧库新库都要写。考虑到性能,可异步写新库,只要保证旧库写成功即可。但注意,要将写新库失败的数据记录在单独日志,方便后续补数据,保证新、旧库数据一致性
- 然后开始校验数据
由于DB数据量很大,全量数据校验不现实。可抽取部分数据,具体数据量依总体数据量而定,只要保证这些数据一致即可。推荐你在未开始迁移数据前,先写好数据校验工具或脚本,在测试环境上测试后,再正式迁移。 - 若一切顺利,即可将读流量切到新
采用灰度切换,比如开始切换10%流量,没有问题再切50%流量,最后再切到100%。 - 由于双写,所以在切换过程中出现任何的问题,都可将读写流量随时切到旧库,保障系统性能
- 观察几天,发现数据迁移没问题后,即可将DB双写改造成只写新库,数据迁移就完成了。
若将数据从自建机房上云,也可用该方案,只是要考虑:自建机房到云上的专线的带宽和延迟,需尽量减少跨专线的读操作,所以在切换读流量的时候你需要保证自建机房的应用服务器读取本机房的数据库,云上的应用服务器读取云上的数据库。这样在完成迁移之前,只要将自建机房的应用服务器停掉并且将写入流量都切到新库就可以了。
- 从机房上云的数据迁移方案
这是种通用方案,无论迁移MySQL还是Redis甚至MQ的数据,都可使用该方案。
FAQ
“双写”方案中主从同步和双写冲突的问题,方案是双写前要将同步断掉。
假设在夜间操作,不考虑主从延迟过高。
但
- 断掉同步
- 有双写代码的应用,被部署上线能正常执行双写
这两个操作之间:
- 先断掉同步,就意味着可能从库丢掉部分数据
- 先上线新代码,就意味着短时间内数据要么冲突,要么对于不幂等的操作,数据变成双份了
双写方案的一种隐患是新旧二库在遇到同一资源的并发操作时,执行顺序有可能不一样,进而结果就不一样。
现代的应用部署都是基于灰度的,在写操作的代码发生切换时(从双写到写新库,或者从写旧库到写新库),做灰度发布都会带来问题吧?
双写时加开关,默认关闭双写。
上线完成后关闭同步,同时打开开关,在低峰期,数据丢失的概率不高。
再配合数据校验的工作,即可保证一致性。
“双写之前要将同步断掉”, 什么时候将同步断掉呢? 数据在不断的写入, 在将同步断掉之后和开启双写之前如果有数据写入如何处理?
所以要做数据的校验和补写,一般双写会加开关,在断掉同步时马上打开双写开关,时间窗口短,数据丢失的不会很多。
1.数据同时写入两个数据库怎么做对代码的改动比较小呢?有成熟的工具或中间件来做吗?
2.新库在同步追上旧库的binlog后,在开始双写时需要断开吗?不然对于新库会有重复的数据。如果新库需要停止对旧库的binlog同步,和双写的开启时机这里怎么做协调呢?
- 还真没有,其实改动代码也简单
- 需要断开的,可以在双写的时候加开关,断开同步时立刻打开开关
好处
迁移的过程可以随时回滚,将迁移的风险降到了最低
缺陷
时间周期比较长,应用有改造的成本。
基于双写的方案我总结了2种实现:
1、基于同步写新库和旧库方案
在写入数据的时候,同步写入旧库数据,异步写入新库数据。
数据校验,对部分数据进行校验(最容易出问题的地方,需要提前准备好脚本)。
使用灰度发布方式将读流量切换到新库。
观察几天没问题后,可以改成只写新库。
2、基于Canal的迁移方案
将新库配置为源库的从库,同步数据。比如使用Canal同步数据。
数据校验,对部分数据进行校验(最容易出问题的地方,需要提前准备好脚本)。
使用灰度发布方式将读流量切换到新库。
暂停应用,将新库作为主库写入,使用Canal同步到旧库。
观察几天没问题后,撤销Canal的同步。
级联同步
也简单,适合数据从自建机房向云上迁移。迁移上云最担心云上环境和自建机房环境不一。
所以我们会在自建机房准备一个备库,在云上环境上准备一个新库,通过级联同步,在自建机房留下一个可回滚DB
步骤
- 先将新库配置为旧库的从库,用作数据同步
- 再将一个备库配置为新库的从库,用作数据的备份
- 等到三个库的写入一致后,将数据库的读流量切换到新库
- 然后暂停应用的写入,将业务的写入流量切换到新库(由于这里需要暂停应用的写入,所以需要安排在业务的低峰期)