数据迁移怎么做到不停机?双写、CDC、校验、切换一次讲清

楼主
学无止境,精益求精

很多企业做数据库升级、系统重构、上云迁移时,真正难的其实不是“怎么把数据搬过去”,而是:

业务不能停,数据还要继续变。

订单还在生成,库存还在扣减,客户还在付款,后台可能还有对账、结算、调度任务持续运行。只要旧系统仍然对外提供服务,迁移期间的数据就不会静止。

这也意味着,数据迁移不能再简单理解成一次“导出—导入”。

真正的难点在于:

一边要把过去几年甚至十几年的历史数据搬到新系统,一边还要接住迁移过程中不断产生的新数据;

等真正准备切换时,还必须证明新旧两边已经基本一致。

否则即使99.9%的数据已经迁完,只要最后那0.1%里恰好包含订单、资金、库存等核心数据,整个切换依然可能失败。

所以,不停机迁移本质上不是一次数据搬运,而是让新旧两套系统在并行运行的一段时间里,持续缩小数据差距,直到最终能够安全完成交接。

图片
在正式展开之前,我整理了《数据仓库建设解决方案》,里面梳理了不少企业在数据集成、数据同步、数仓建设和数据平台搭建过程中常见的场景与实践思路。
 
如果你最近也在规划相关项目,可以结合自己的系统架构和数据现状一起参考。
 
需要自取:https://s.fanruan.com/xwgup
图片

而回到数据迁移本身,不停机真正要解决的,其实就是四件事:

存量怎么搬、增量怎么追、两边怎么证明一致、最后怎么安全切过去。

一、不停机迁移的本质,是让新旧数据差距不断归零

传统的数据迁移一般是:

停业务 → 导出旧库 → 导入新库 → 校验 → 启动新系统。

这种方式逻辑很简单,但停机时间基本等于:

全量迁移时间 + 数据校验时间 + 系统切换时间。

如果数据库只有几十GB,可能还能接受;但当数据量来到TB级,或者涉及订单、支付、库存等核心系统,停机几个小时往往意味着业务直接中断。

所以不停机迁移的第一步,是把数据拆成两部分。

一部分是存量数据,也就是迁移开始之前已经存在的数据;

另一部分是增量数据,也就是全量迁移期间,旧系统继续产生的新增、修改和删除。

这里最重要的不是“先跑全量还是先跑增量”,而是必须找到一个明确的数据边界。

例如,在全量开始时记录一个数据库快照以及对应的日志位点:

这个位点之前的数据由全量迁移负责,这个位点之后的数据由CDC持续追。

如果没有这个边界,就很容易出现迁移空档。

比如一条订单在全量开始后创建,随后发生支付、修改地址、退款。如果全量跑完以后才开始增量同步,中间几次状态变化到底有没有完整迁过去,很难证明。

所以更成熟的思路通常是:

先建立增量捕获能力,再开始搬存量,最后让增量持续追赶。

而且现实中的存量迁移往往也不是简单复制。如果新系统已经调整了数据模型,还要处理字段映射、格式转换、编码统一以及历史脏数据清洗。

这类场景可以放到FineDataLink 5.0的数据开发任务中处理:

先从旧库抽取数据,再按照新系统的数据模型完成清洗、转换后写入目标库。

这样后续某批历史数据出现异常时,也更容易判断问题出在抽取、转换还是写入环节,而不是重新翻一堆一次性迁移脚本。

二、双写看起来最直接,却最容易变成一致性问题

很多人听到“不停机迁移”,第一反应就是:

新旧数据库一起写不就行了?

也就是所谓双写。

例如用户提交订单以后:

先写旧库,再写新库。

表面上看只是多执行了一次数据库操作,真正运行起来马上就会遇到问题。

  • 旧库成功,新库失败怎么办?
  • 重新执行以后,新库成功了,旧库会不会重复生成订单?
  • 两边都写成功,但其中一个事务晚几秒提交,哪个状态才算最终态?

所以双写真正难的不是“写两次”,而是同时处理:

幂等、重试、失败补偿、事务边界以及权威数据源。

尤其在迁移过程中,一定要明确:

到底哪一个系统是Source of Truth,也就是最终事实来源。

比如正式切换之前仍然规定旧库为主库,那么即使新库已经可以使用,也不能允许两个系统分别修改同一条业务数据。

否则旧库把订单金额改成100元,新库又改成120元,两边都有完整操作记录,但系统已经无法判断谁才是真实结果。

因此,大多数数据库迁移并不需要长时间双写。

更常见的模式其实是:

业务继续单写旧库,CDC负责把变化复制到新库。

只有在最终切换窗口,新旧系统确实需要短时间共同承接写入时,才考虑双写。

如果必须双写,更稳妥的方式通常也不是让一个请求强行完成两个数据库事务,而是把主事务先可靠落库,再通过消息队列、Outbox事件表、重试补偿把变化送到另一端。

这样即使目标端暂时失败,也还能通过事件ID重新执行,并利用业务唯一键保证幂等。

三、CDC真正解决的是:迁移期间数据一直在变

CDC,全称Change Data Capture,也就是变更数据捕获。

它关注的不是“这张表现在有哪些数据”,而是:

数据库刚刚发生了什么变化。

以MySQL为例,INSERT、UPDATE、DELETE都会留下binlog记录。CDC可以沿着数据库日志持续消费这些变化,再把它们应用到目标端。

这和传统的更新时间增量不是一回事。

很多企业原来做数据同步,会写:

WHERE update_time > 上次同步时间

但这种方式存在不少边界问题。

比如:

  • 事务23:59:59开始,00:00:03才提交,到底属于哪一批?
  • 数据库服务器时间不一致怎么办?
  • 一条数据已经被DELETE了,又从哪里查询它的update_time?

所以对于不停机迁移,CDC更适合承担持续追数的任务。

但CDC也不是启动以后就可以不管。

至少要持续关注四个问题。

第一,业务系统不是为分析设计的。

任务中断以后必须知道上次消费到了哪里,否则可能漏数据,也可能重新消费大量历史日志。

第二,事务顺序。

同一个订单先创建、再支付、再退款,目标端不能先收到退款,再收到支付。

第三,DDL变化。

如果迁移过程中源库突然新增字段或者修改字段类型,目标端没有同步调整,后续数据就可能直接写入失败。

第四,同步积压。

如果源库每秒产生2万条变化,而目标库只能写入1万条,那么即使任务一直显示运行中,两边的数据差距也会越来越大。

所以真正决定能不能切库的,不是:

CDC有没有启动。

而是:

旧库和新库之间还差多少,这个差距是在扩大还是缩小。

到了增量阶段,FineDataLink 5.0更适合利用CDC持续捕获数据库的新增、修改和删除,把变化追到目标端。

迁移团队真正应该盯的是源端变化速度、目标端写入速度以及当前积压量,只有差距不断收敛,新库才真正接近可切换状态。

四、数据校验不能只看COUNT(*),至少要做三层

不少迁移项目到了最后,会执行一句:

旧库COUNT() = 新库COUNT()。

数字一样,就宣布迁移成功。

这其实非常危险。

假设旧库少了一条订单,新库又恰好多了一条重复订单,两边总行数仍然一样。

所以真正的迁移校验至少应该分三层。

1.结构校验

检查:

表、字段、数据类型、主键、默认值、字符集、时间精度。

例如源库字段是decimal(18,4),目标库变成decimal(18,2)。

数据一条没少,但金额精度已经发生变化。

2.数据校验

除了COUNT,还应该比较:

主键范围、分区行数、空值数量、最大最小值、金额汇总,以及关键字段Hash或Checksum。

对于亿级数据,也没有必要每次全部逐行对比。

可以先按照日期、分区或者ID范围分桶。

先发现“8月份数据不一致”,再缩小到“8月20日”,最后定位到具体ID区间。

3.业务校验

最后还要验证业务关系。

例如:

  • 订单金额能不能和明细汇总对应?
  • 已支付订单是不是都有付款记录?
  • 退款状态能不能找到退款流水?
  • 库存扣减能不能对应出库记录?

因为技术层的数据一致,不代表业务层一定正确。

而且校验还有一个重要前提:

必须比较同一时间边界的数据。

旧库还在不断写,新库也在持续追。如果旧库统计到了10:00:10,而新库只同步到10:00:05,两边出现差异本来就是正常现象。

FineDataLink 5.0的数据检测能力也可以先对关键字段、异常值和数据明细做规则检查,把问题范围缩小,再由业务进一步核对订单、资金、库存等关系。

换句话说:

CDC回答的是“数据有没有过来”,数据检测回答的是“过来的数据能不能信”。

五、真正的切换,不只是修改一个数据库地址

当全量完成、CDC持续运行、数据校验基本通过以后,才真正进入风险最高的一步:

切换。

切换前至少应该满足三个条件:

  • 存量迁移已经完成;
  • 增量延迟已经接近零;
  • 核心业务表校验已经通过。

然后进入最终收口阶段:

冻结高风险DDL和批量任务 → 等待CDC追平 → 记录最终日志位点 → 最后一次校验 → 切读流量 → 切写流量。

为什么一定要记录最终日志位点?

因为切换真正需要回答的是:

旧库最后一笔有效事务,新库到底有没有收到?

只有这个边界明确,后面发现数据差异时,才能判断到底是CDC遗漏、历史数据问题,还是切换以后才产生的新数据。

而且数据库已经追平,并不代表应用一定可以直接100%切过去。

新环境还可能出现:

SQL兼容问题、索引遗漏、慢查询、连接池异常、缓存失效、定时任务重复执行。

所以更稳妥的做法通常是灰度切换。

例如先切查询流量,再切低风险业务,最后切核心交易写入;或者按照地区、租户、业务线逐步放量。

切换当天往往还会同时涉及补数、校验、SQL处理、同步和结果检查等多个步骤,问题有时并不是某个任务失败,而是上下游执行顺序错了。

这时候可以把相关数据任务放到FineDataLink 5.0里统一编排,设置任务依赖、失败重跑和结果通知。

它并不替代应用侧的流量切换,但可以让数据团队在正式放量之前先确认:该完成的数据任务是否已经结束,关键检查是否通过,还有没有异常没有处理。

六、真正成熟的迁移,一定提前设计回滚

很多迁移方案把切换步骤写得非常详细,但回滚方案只有一句:

出现异常,切回旧库。

真正出问题时,这句话往往执行不了。

假设新库已经运行30分钟,这期间产生了10万条新订单。

如果这些订单只写在新库里,此时直接把应用切回旧库,相当于业务瞬间回到了30分钟前。

所以回滚方案必须提前回答几个问题:

  • 新库产生的新数据怎样补回旧库?
  • 是做反向CDC、消息重放,还是业务补偿?
  • 哪些指标触发回滚?
  • 谁有权限做最终决定?
  • 旧库需要保留多久?
  • 什么时候正式关闭回滚窗口?

因此,真正成熟的迁移不会把“切换”理解成某一个瞬间。

它应该是一个完整阶段:

切换前准备 → 灰度切换 → 稳定观察 → 关闭回滚窗口 → 旧系统下线。

在稳定观察期,还要持续关注:

应用错误率、接口延迟、慢SQL、核心业务指标、CDC积压以及数据差异。

只有新系统持续稳定,数据差异逐步归零,回滚链路已经不再需要,才意味着这次迁移真正完成。

结语

数据迁移做到不停机,靠的从来不是某一个神奇命令,而是一套不断让新旧系统差异收敛的机制。

  • 全量迁移负责历史数据,
  • CDC负责追增量,
  • 双写解决特殊并行窗口,
  • 校验负责证明一致,
  • 灰度切换负责降低上线风险,
  • 回滚机制负责最后兜底。

真正应该追求的,也不是形式上的“0秒停机”。

而是把业务中断时间、数据不一致风险以及切换过程中的不确定性控制在企业可以接受的范围内。

如果只记住一句话,可以记住:

迁移成功的标志,不是新库里已经有数据,而是从一个明确的数据边界开始,每一笔业务变化都能说清楚:

发生在哪里、同步到了哪里、是否一致,以及出问题以后怎么恢复。

分享扩散:

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

返回顶部 返回列表