数据迁移套路总结

数据迁移套路总结

去年年中的时候做了一个虚拟机到K8S的应用迁移,迁移的过程中把数据库也进行了迁移,这里总结一下数据迁移的套路。

业务场景

我所经历过的数据库迁移的业务场景就是机房迁移,或者说是整个生产环境搬迁。这时候需要把原有的数据从一个库中迁移到另外一个库,表结构不变,

数据迁移大体上有两种做法,一种是追日志,另一种是双写。

追日志法

追日志具体的做法如下:

  1. 开发一个可以记录数据库变更的日志,记录所有的增删改同时还可以回放这些记录的工具。这个程序可以在业务代码里开发,也可以基于MYSQL的binlog来做。去年我做的项目便是DBA团队基于MYSQL binlog开发的记录工具。

  2. 开启数据库变更记录程序,接着把旧库的数据库按照主键一批一批的select出来,然后批量插入新库。

  3. 第2步做完之后,开始在新库中回放第一步程序记录的日志。

  4. 由于旧库一直在写,新库一直在同步,怎么也无法追上,所以切流量之前需要把旧库设置为readonly,这时就能让数据追上了。然后把数据库连接指向新的数据库即可。

这里需要特别注意第三步的回放需要保证幂等。具体来说就是update的时候需要比较更新前的数据是否一致,如果不一致则不回放。对于insert而言,如果新库已经存在则需要删除掉对应记录再insert,新库数据不存在则可以直接insert。
对于具体的实现,如果是解析mysql的row格式binlog则可以获取到update前后的数据,如果是自行在业务中记录,则需要特别注意这一点。

追日志的优点实现起来比较简单,如果有binlog订阅工具就不需要修改任何应用代码即可完成迁移。缺点则是有短暂的停机时间,也就是第4步的readonly的时间。

数据库双写

双写的具体做法如下:

  1. 预先开发一个数据比对和修正工具, 该工具可以从两个数据库中读取数据并进行比较是否一致,并使用较新的值修改旧的值。

  2. 在写数据库的时候同时写新旧两个库,需要注意两点:

    1.1 insert的时候新旧两个库都插入,这里需要注意主键最好由应用生成,如果旧库已经选择了数据库的自增id,那么插入新库的时候必须用旧库所生成的id。

    1.2 update的时候,如果新库有数据则update,没有则什么也不做。

  3. 把旧库的数据按照主键一批一批的select出来,然后批量插入新库。如果新库中已经有了对应的数据则放弃插入,只插入旧库有而新库没有的数据。这一步执行完以后,新库就有了旧库里面所有的数据。

  4. 使用步骤1中开发的工具,核对两边数据是否完全一致。核对完成后断开旧库链接,只读写新库。

这种办法无法保证数据完全一致,由于在第2步和第3步是由两个进程同时执行,假如有一个update来了,迁移进程(第3步)select了一批数据,这批数据还没传输到新库,update双写执行了,这时由于新库还没有数据所以什么也没发生,接着迁移进程把数据写进去了。这就相当于写了一个旧数据到新数据库中了。如下表所示:

旧库 新库 备注
迁移进程select旧库 读出id from 100 to 200
update id=150 id=150 (数据不存在)
迁移进程insert新库 来自旧库的id=100 to 200 对于id=150的更新丢失

所以需要使用1中开发的对比工具进行全量对比,把不相同的找出来进行更新。

双写的优点是没有停机时间,缺点是需要修改代码进行双写。而且双写还是要处理好旧库对应的事务操作,还是比较麻烦的。个人推荐这种办法进行日志型数据的迁移,比如【我的最近浏览记录】这种业务。这类数据基本只有insert,处理起来比较简单。对于这种业务,双写还支持异构数据的迁移比如MYSQL迁移到HBASE/Cassandra,17年的时候有个业务由于MYSQL容量不够迁移到HBASE就是使用这种办法迁移的。那个业务是一个【最近联系人】的场景,只需要保留近3天的数据即可。

总结

本文介绍了两种数据迁移的办法,并简单描述了不同方案的优缺点和实现细节,希望对你有所帮助。