注:没有实践过,完全是为了学习
目的
目的:将A库中N张表中的数据迁移到B库的M张表中,在B库中做分表操作。在迁移过程中要求不停服。
方案
方案:双写
具体流程
-
在B库中建表,在代码中实现双写的开关以及功能(读老库,增删改老库和新库)
- 由于只读老库,所以新库中的功能实际上未对外提供数据,即使期间新库数据存在问题,依旧不影响服务功能
- 新增操作:老库和新库都新增,因此新增的数据新老库是一致的。(新老库自动生成的主键不一致问题,可以先插入老库,得到id,再插入新库,插入数据带上id)
- 删操作:删除操作幂等,老库数据删除;无论新库中是否存在该数据,删除操作后新库都不存在该条数据
- 更新操作:老库数据更新;新库中若存在该条数据,则更新,新老库数据一致,若不存在该条数据,新库中没有该条数据
- 注意,即使新库不对外提供数据,但依旧可以灰度执行
-
双写执行一段时间后,对比新老库数据,要求新老库对应主键的数据一致
- 数据一致的维度:存储维度,即新老数据完全一致;业务维度:用户看到的数据是一致的
- 存储维度数据一致的条件是什么:对应主键数据的所有字段完全一致?(update_time字段是在MySQL机器上生成的,可能不一致,是否需要在代码逻辑中传入该字段?)
- 回答上一个问题:迁移前必须保证机器时间一致;网络延时,新库数据update_time可能会大于老库数据;所以校对数据不必对比update_time,或者设置update_time字段差别不能超过某个阈值
-
若双写没问题,则可以开始搬迁历史数据至新库
- 批量迁移,然后验证数据,验证数据的方式与上述一致
- 迁移过程中,如果同一个id新老库中都有这条数据,那么需要对比数据的update_time字段,老数据不能覆盖新数据。
- 第一种情况:A库中的某条数据的update_time<B库中的update_time,这表示A库中的该条数据较老,不将此条数据写入新库,出现此情况的原因可能是:从A中读了数据data1,然后业务逻辑又写了AB中的该条数据,然后在将data1与B库中的该条数据比较时,发现数据较老
- 第二种情况:A库中的某条数据的update_tim>B库中的update_time,那么将此条数据写入B库中。出现此情况的原因可能是:业务双写,先写A,迁移读了A准备写入B,双写的B还未完成。次时将A中数据写入B库,双写过程中的写B过程可能由于不满足where条件而不执行,也可能会再次执行,有update操作幂等,因此没有问题。(在写SQL时要保证一条SQL时幂等的,不能出现a=a+1的操作,若有此操作,必须使用乐观锁)
- 第三种情况:update_time一致,不做修改
- 第四中情况:A中的某条数据B中没有,直接写入B库
- 多次批量迁移和对比完成后,新老库的数据应该一致
-
切读:从读旧库到读新库。此阶段:读新,写旧,写信
- 切读也可灰度
- 验证切读没有问题,此阶段可能需要一段的时间,验证切读逻辑正确性,验证机器压力等指标数据正常
-
清理旧数据,旧代码逻辑
- 清理代码中迁移中针对老库的读写代码,开关代码
- 清理老库数据
大致流程图:
详细流程
问题
- update_time字段通常是
ON UPDATE CURRENT_TIMESTAMP
,如果库AB的机器时间不一致,或者网络时延导致同一操作update_time字段不一致如何解决
答:机器时间提前校准 - 先写A后写B,B的数据update_time可能大于A,如果一个逻辑:写A,时间戳为1,写B时间戳4;又来一个逻辑,写A时间戳2,写B失败,那么B的数据将按照时间戳后与A,此时恰好历史数据搬迁则会出问题?
答:双写是否需要分布式事务保证?手动补偿? - 如果存在外键约束,新增数据可能失败
答:一般生产环境不允许存在外键,如果存在外键,写失败也无妨,迁移历史数据时,按照依赖拓扑图迁移? - 迁移前后数据结构不一致问题
答:在DAO层,做转换,统一视图,迁移前后的数据库不一致,例如从MySQL迁移至HBase,同数据源不影响上层使用 - MySQL是否存在搬迁老数据的工具?存在,例如mysqldump等,可以直接让DBA迁移
- 如何快速配置、回滚到各个阶段?
答:使用策略模式,配置中心
注意:该方法是调研结论,但实际上笔者并未真实使用过。
参考 https://blog.csdn.net/ronmy/article/details/65649600