3. CDC 技术选型:三种主流方案深度对比

CDC(Change Data Capture)说白了就是「抓变化」的技术。主数据实时性靠的就是它。我这些年经手过不少项目,选错 CDC 方案导致返工的情况,见过太多次了。

今天咱们就把三种主流方案掰开揉碎讲清楚:基于日志的 CDC、基于触发器的 CDC、基于时间戳的 CDC。每种方案我都会结合自己的踩坑经历来说。

3.1 基于日志的 CDC(Debezium 为代表)

这是目前最主流的方式。Debezium 读取数据库的 binlog(MySQL)或 WAL(PostgreSQL),解析成事件流。你想想看,数据库本身写日志就是为了恢复和复制,我们只是「蹭」了一下这个机制。

核心原理:数据库把每次变更写入事务日志,CDC 工具作为「偷听者」实时读取并解析这些日志,然后推送给下游。

优点

  • 对业务零侵入:不需要改表结构,不需要加字段,不需要写触发器代码。我接手过一个遗留系统,连源码都找不到了,用 Debezium 直接读 binlog 就把数据同步做起来了。
  • 捕获完整变更历史:你能拿到「改前值」和「改后值」。做审计、做回滚、做历史追溯,这个能力太重要了。
  • 低延迟:毫秒级。日志一落盘,CDC 就能读到。我见过一个实时风控场景,从 MySQL 变更到 Kafka 消费,端到端延迟不到 50ms。
  • 不增加数据库负担:读日志是顺序 I/O,对源库几乎无影响。不像触发器那样每笔操作都要额外执行一段逻辑。

缺点

  • 日志格式依赖数据库版本:MySQL 5.6 和 5.7 的 binlog 格式有差异,8.0 又变了。升级数据库版本时,CDC 工具也得跟着适配。
  • 需要开启数据库相关配置:比如 MySQL 要开 binlog,且格式得是 ROW。有些 DBA 出于安全考虑不愿意开,你得去沟通。
  • 初始快照需要额外处理:Debezium 第一次启动时要做全量快照,如果表很大(比如上亿行),这个阶段会比较耗时。

我的经验:Debezium + Kafka 是黄金组合。但要注意,如果下游消费速度跟不上日志产生速度,Kafka 的磁盘会爆。我曾经在双十一大促时遇到过这个问题,后来加了限流和监控告警才稳住。

3.2 基于触发器的 CDC

这是「老派」做法。在源表上建 INSERT、UPDATE、DELETE 触发器,把变更记录写到一张专门的变更日志表里。然后另一个进程轮询这张表,把数据取走。

注意:触发器是数据库内部机制,和业务逻辑耦合在一起。一旦触发器出问题,可能直接影响源表的正常写入。

优点

  • 数据库无关:只要支持触发器,任何数据库都能用。Oracle、SQL Server、MySQL、PostgreSQL 都行。
  • 可以自定义捕获逻辑:比如只捕获某些字段的变化,或者只捕获特定条件的变更。灵活性很高。
  • 不需要额外配置:不用开 binlog,不用改数据库参数。对于管控严格的数据库环境,这是唯一的选择。

缺点

  • 性能开销大:每笔 DML 操作都要额外执行触发器逻辑,写入变更表。我见过一个案例,加了触发器后,源库的写入性能下降了 30%。
  • 对源库有侵入:要建触发器、要建变更表、可能要加字段。业务团队往往很反感这种改动。
  • 无法捕获 DDL 变更:触发器只能捕获数据变更,表结构变更(ALTER TABLE)是抓不到的。
  • 事务一致性难保证:触发器和源操作在同一个事务里,但如果下游消费失败,变更表里的数据可能被重复消费或丢失。

避坑指南:我曾经在一个金融项目里被迫用触发器方案(DBA 死活不开 binlog)。我的建议是——如果非用不可,一定要给变更表加分区,定期清理历史数据。不然这张表会膨胀到让你怀疑人生。

3.3 基于时间戳的 CDC

这是最「朴素」的方式。在源表上加一个 last_modified 或 update_time 字段,然后定时轮询:SELECT * FROM table WHERE update_time > last_poll_time。

嗯,说白了就是「定时查一下哪些数据变了」。简单粗暴,但坑也不少。

优点

  • 实现极其简单:一条 SQL 就能搞定。不需要额外组件,不需要复杂配置。
  • 对数据库无额外压力:相比触发器,它不会影响写入性能。相比日志 CDC,它不需要解析复杂格式。
  • 易于理解和维护:任何开发人员都能看懂。出了问题排查也方便。

缺点

  • 无法捕获删除操作:数据被删了,时间戳字段也没了。你根本不知道它曾经存在过。除非你用软删除(逻辑删除)。
  • 时间戳精度问题:如果同一秒内有多条记录变更,你可能会漏掉。MySQL 的 datetime 精度到秒,高并发场景下根本不够用。
  • 轮询延迟:轮询间隔决定了延迟。间隔 1 秒,延迟就是 1 秒;间隔 1 分钟,延迟就是 1 分钟。实时性要求高的场景不适用。
  • 需要业务配合加字段:源表必须要有时间戳字段,而且业务代码必须正确维护它。我见过不少「忘记更新 update_time」的 bug。

适用场景:数据量不大、实时性要求不高(分钟级)、可以接受软删除、业务团队愿意配合加字段。说白了,就是「低成本、低要求」的方案。

3.4 三种方案对比总表

对比维度 基于日志(Debezium) 基于触发器 基于时间戳
对业务侵入 无 高(需建触发器、变更表) 中(需加时间戳字段)
性能影响 几乎无 较大(额外写入开销) 低(轮询查询开销)
延迟 毫秒级 秒级(取决于消费速度) 分钟级(取决于轮询间隔)
捕获删除 支持 支持 不支持(需软删除)
捕获 DDL 部分支持 不支持 不支持
变更历史 完整(改前改后值) 可自定义 仅最新值
实现复杂度 高(需部署 Kafka、Connector) 中 低
运维成本 高(监控日志、管理偏移量) 中(清理变更表) 低
数据库兼容性 依赖数据库版本 广泛 广泛

3.5 选型决策逻辑

我个人习惯按这个顺序来决策:

  1. 先看数据库是否支持日志 CDC。如果支持,且 DBA 愿意配合开 binlog/WAL,优先选 Debezium。这是最省心、最强大的方案。
  2. 如果数据库不支持或不让开日志,再看实时性要求。要求秒级以内?那只能上触发器,虽然痛但能忍。
  3. 如果实时性要求不高(分钟级),且业务团队愿意配合加字段,时间戳方案是最轻量的选择。
  4. 如果既要捕获删除,又不能加字段,那触发器是唯一选择。

我的建议:能上日志 CDC 就上日志 CDC。别因为「简单」去选时间戳方案,后面改起来成本更高。我见过一个项目,一开始用时间戳,后来要支持删除和实时同步,硬生生重构了三个月。

3.6 三种方案架构对比图

下面这张图能帮你直观理解三种方案的数据流向差异:

三种 CDC 方案架构对比 三种 CDC 方案架构对比 方案一:基于日志 CDC 源数据库 Binlog / WAL Debezium Connector Kafka Topic 下游消费者 方案二:基于触发器 CDC 源数据库 触发器(Trigger) 变更日志表 轮询消费程序 下游消费者 方案三:基于时间戳 CDC 源数据库 update_time 字段 定时轮询任务 上次轮询时间戳 下游消费者 关键差异总结: • 日志 CDC:无侵入、低延迟、完整历史,但依赖数据库版本和配置 • 触发器 CDC:数据库无关、可自定义,但性能开销大、侵入性强 • 时间戳 CDC:实现简单、运维成本低,但无法捕获删除、延迟较高

3.7 我的最终建议

选 CDC 方案,本质上是在「侵入性」「性能」「实时性」「复杂度」之间做权衡。没有银弹。

但我个人的经验是:优先用日志 CDC。Debezium 生态已经非常成熟,社区活跃,坑也填得差不多了。如果实在因为环境限制用不了,再退而求其次。

触发器方案我建议只作为「过渡方案」或「补充方案」。比如日志 CDC 抓不到某些自定义逻辑,可以用触发器补一下。但别把它当主力。

时间戳方案嘛……嗯,如果你的数据量不大、实时性要求不高、团队资源有限,用它快速上线是可以的。但一定要想好后续怎么迁移到更靠谱的方案。