增量迁移的基石:什么是CDC?

聊增量迁移,绕不开一个核心概念——CDC。

CDC,全称 Change Data Capture,中文叫「变更数据捕获」。说白了,就是实时捕捉数据库里的数据变化。插入、更新、删除,每一条操作都能被捕获到。

我刚开始接触这个术语时,觉得它挺玄乎。后来做了一次全量重跑,几十亿数据跑了整整两天,业务方差点没把我电话打爆。嗯,从那以后,我彻底理解了CDC的价值。

CDC的核心作用就一个:让下游系统只处理变化的数据,而不是全量数据。你想想看,如果每次同步都全量拉一遍,数据量小还行,上了TB级别,谁也扛不住。

CDC的本质:把数据库的增量变更,变成一条条可消费的事件流。

CDC的三种常见实现方式

实现CDC的方式有好几种,但真正在生产环境里常用的,就三种。我按推荐程度排个序,咱们一个一个说。

1. 基于日志的CDC

这是我最推荐的方式,也是目前大厂的主流选择。

原理很简单:数据库本身就有事务日志(比如MySQL的binlog、PostgreSQL的WAL、Oracle的Redo Log)。这些日志记录了每一次数据变更的完整信息。CDC工具直接读取这些日志,解析出变更事件,然后推送给下游。

为什么我偏爱这种方式?因为它对业务代码零侵入。不需要改表结构,不需要加触发器,不需要改应用程序。数据库该怎么跑还怎么跑,CDC工具在背后默默干活。

我的经验:在MySQL场景下,我习惯用Canal或Debezium来消费binlog。Debezium配合Kafka,基本是业界标准方案。记得把binlog格式设为ROW模式,否则你拿到的可能是模糊的SQL语句,而不是精确的变更数据。

基于日志的CDC有几个明显优势:

  • 实时性高:日志一写,CDC立马就能读到,延迟通常在毫秒级
  • 不丢数据:日志是数据库持久化的一部分,只要日志在,数据就不会丢
  • 性能影响小:读取日志是文件级别的操作,对数据库本身几乎无压力

当然,它也有缺点。比如,需要额外的组件来解析日志,运维成本稍高。另外,如果数据库版本升级,日志格式可能变化,CDC工具也得跟着适配。

2. 基于触发器的CDC

这种方式,说白了就是在表上建触发器。每次有数据变更,触发器就把变更记录写到一张专门的变更表里。CDC工具再去读这张变更表。

我在早期项目中用过这种方式。当时数据库不支持binlog,没办法,只能上触发器。

我曾经踩过的坑:触发器方式最大的问题是性能。有一次我在一张高频写入的表上加了触发器,结果数据库的写入性能直接掉了30%。触发器是在事务内执行的,它会拖慢每一次DML操作。如果你的表写入量很大,慎用。

基于触发器的CDC优缺点很明显:

优点 缺点
实现简单,不依赖外部组件 对数据库性能有明显影响
兼容性好,几乎所有数据库都支持触发器 需要额外维护变更表,增加存储开销
可以自定义捕获逻辑 触发器本身也是代码,有维护成本
如果触发器逻辑出错,可能影响主库写入

我个人建议,除非实在没办法,否则尽量别用触发器做CDC。它更适合那种数据量不大、对实时性要求不高的场景。

3. 基于时间戳的CDC

这种方式最「朴素」。在表里加一个时间戳字段(比如 update_time),每次更新数据时更新这个字段。CDC工具定期扫描这张表,根据时间戳筛选出最近变更的数据。

你可能会问:「这也能叫CDC?」嗯,严格来说它确实算一种增量捕获方式,只是比较粗糙。

适用场景:数据量小(百万级以下)、对实时性要求不高(分钟级或小时级)、没有复杂删除操作的场景。

我在一个内部管理系统里用过这种方式。数据量不大,每天变更也就几千条,用时间戳完全够用。但如果你要处理的是核心交易数据,千万别这么干。

基于时间戳的CDC有几个硬伤:

  • 无法捕获删除操作:删掉的数据,时间戳也没了,你根本不知道它被删过
  • 时间精度问题:如果同一秒内有多条数据变更,可能漏掉一部分
  • 扫描压力大:每次都要全表扫描或索引扫描,数据量大了根本扛不住
  • 延迟较高:扫描周期决定了延迟,做不到秒级

下面这张图,我把三种方式的对比画了出来,方便你直观理解:

CDC三种实现方式对比 基于日志的CDC 读取数据库事务日志 (binlog / WAL / Redo Log) 解析出精确的变更事件 优势 实时性高 · 不丢数据 性能影响小 · 零侵入 基于触发器的CDC 在表上创建触发器 变更时写入变更表 CDC工具读取变更表 劣势 影响数据库写入性能 额外维护变更表 基于时间戳的CDC 表中增加时间戳字段 定期扫描筛选变更 根据时间戳增量拉取 劣势 无法捕获删除操作 延迟高 · 扫描压力大 推荐优先级:基于日志 > 基于触发器 > 基于时间戳 选型建议 • 核心交易系统、大数据量、高实时性 → 基于日志(首选) • 数据量小、无日志可用、能接受性能损失 → 基于触发器(备选) • 数据量极小、分钟级延迟可接受、无删除操作 → 基于时间戳(临时方案)

三种方式怎么选?

说实话,没有银弹。选哪种,取决于你的场景。

我个人习惯是这样的:

  • 新项目:直接上基于日志的CDC。Debezium + Kafka 是标配,MySQL用Canal也行。别犹豫,这是最省心的路。
  • 老系统改造:如果数据库不支持日志解析,或者DBA不让开binlog,那就用触发器。但一定要做好性能压测,别上线了才发现扛不住。
  • 临时方案或小项目:时间戳方式可以快速上线。但记住,这只是过渡方案,别想着一直用下去。

我的建议:如果你刚开始做增量迁移,优先考虑基于日志的CDC。虽然前期搭建成本高一点,但后期运维省心太多。我曾经在一个项目里先用时间戳凑合,结果数据对不上,排查了三天才发现是时间戳精度问题。后来换成binlog,再也没出过类似问题。

好了,CDC的概念和三种实现方式就聊到这儿。记住一句话:CDC是增量迁移的基石,选对方式,后面的事就顺了。