【项目经验】数据迁移总结

在产品迭代初期或者系统重构时期,业务模型的调整带来数据结构的变化,数据迁移不可避免。做好数据迁移需要考虑周全,且准备充分,做好预案,否则如果出现数据不一致问题,纠错成本高,同时核心业务数据的错误,会引起客户/业务方的投诉,团队也会承受巨大的压力。

本文结合最近一个实际项目的数据数据迁移过程,讲述了踩过的坑,加上自己的一些思考得出的一些方法论,最后给出了数据迁移个脚本的一个实例

目标

确保新客户端访问新业务模型时能否正常查询之前的数据,如订单等;不会出现数据不一致。

原则

  • 影响可控 —— 只对需要迁移对数据做修改,不能影响到其他数据;不到万不得已,不会允许停机迁移数据,因此迁移窗口期越短越好,减少迁移窗口期用户行为带来的数据问题;
  • 可回退 —— 一旦发现迁移数据有问题,可以回退到之前的数据状态;
  • 可追溯 —— 出现问题,能够有日志或者备份数据可查;数据库的binlog,迁移程序的log可以作为依据;
  • 可测试 —— 迁移方案必须可测试,要满足可测试,那么迁移方案必须是通用型的方案。

思路

  • 先备份,再迁移;
  • 迁移后需要做数据比对,确保数据一致性;
  • 出现问题,考虑是否做回退【并不是所有场景都能直接回退】;
  • 如果业务量大,为避免用户行为和数据迁移产生冲突,考虑停服务迁移。

步骤

  • 备份 —— 将待迁移数据备份到bak表,任何在迁移过程中会被修改的数据应当被备份,任何在insert场景被当着原数据使用的数据应当被备份;
  • 迁移 —— 迁移脚本 / 程序 只对bak表中的目标数据做操作;
  • 验证 —— 迁移完成后,需要做数据核对,确保数据一致性;
  • 回退 —— 回退脚本同样只对bak表中的目标数据做操作,且行为和迁移脚本行为相反。

方案的选择

数据库脚本

在数据量小,业务场景简单的情况下非常适合,简单轻量级,通过sql脚本完成数据迁移非常合适。但如下几点需要认真思考:

  1. 能否写成通用sql?如果不能放弃;因为为不同环境准备不同的脚本,破坏了‘可测试’这一原则;
  2. 被操作的数据量是否太多?如果过多(通常超过1万条就很多了),可能导致脚本提交超时;
  3. 业务场景是否复杂?如果涉及到的表过多(超过3张),脚本的执行存在先后顺序,这时候通过人为保证,风险会大大增加;
  4. 测试环境和线上环境的sql执行工具/环境是否一致?如果不一致(很多公司的DBA工具会对一些语法和格式作出限制,比如不能有换行,注释中不能有半角分号等),则也会破坏掉‘可测试’这一原则;
  5. 线上sql执行流程是否冗长?如果流程冗长(公司的流程可能要求需要TL和DBA的审批,DBA作为第三方资源依赖不可控),且脚本多,会拉长数据迁移的窗口期,业务风险大大增加,破坏了‘影响可控’的原则。

迁移程序

和‘数据库脚本’方法相反,撰写的‘迁移程序’能够避开这些缺点,更适合于业务场景复杂,数据量大的场景。

方案对比

脚本

程序

备注

通用性

不完全

完全

如:依赖第三方数据时,脚本无法做到通用

复杂场景支持

不适合

适合

复杂业务场景脚本不适合,如循环调用,第三方系统数据,多表依赖等

大数据量支持

不适合

适合

大数据量可能导致脚本提交超时,通常超过1万条不宜使用脚本,大多数dba工具通常也会对操作的数据量做限制

开发成本随复杂度增长

指数

线性

 

无论采用‘数据库脚本’还是‘迁移程序’,都需要遵循上面的‘原则’和步骤。

思考

数据迁移和应用程序发布的先后顺序?

在业务量大的情况下,数据迁移过程中,数据被用户行为修改了怎么办?

迁移失败,什么情况下做回退,什么情况下不能做回退?

踩过的坑

在转赠2.0项目中,前期对数据迁移的业务复杂度预估不足,选择了‘数据库脚本’方式,踩了不少坑:

  1. 在测试环境测试通过的sql脚本,无法直接在线上环境执行,原因是线上数据库工具对sql格式和内容校验更为严格(不允许有换行等);
  2. 测试过程中发现,需要实现为一个订单循环生成多个券码的场景,虽通过sql间接实现,但是复杂度很高,开发成本增大;
  3. 由于sql较多且复杂度高(如:使用insert select),需要经过TL和DBA的双重审批,拉长了数据迁移窗口。

迁移脚本示例

业务场景:将订单表中订单状态为2和3的订单状态更新为4

backup

create table order_bak like order;
insert into order_bak select * from order where status in (2,3);
注:order为业务表,order_bak为备份表;目标是将订单状态为3的订单更新为4。

migration

update order set status = 4 where id in (select id from order_bak);

注:如果直接对order原表进行操作,一旦错误,无法回滚。

check

select count(*) as cont from order where status != 4 and id in (select id from order_bak);

注:如果cont > 1则需要考虑数据迁移是否成功

rollbak

update order o inner join order_bak bak on o.id = bak.id set o.status = bak.status;

注:如果迁移后-->回滚前,有用户行为更改status状态,则不能直接rollback ,需要具体情况具体分析;实在避免不了,且此类数据很多,则考虑停服务迁移了。

posted @ 2018-09-06 09:53  倒骑的驴  阅读(4043)  评论(2编辑  收藏  举报