CDC技术选型对比:四种主流方案的优劣分析

做CDC增量同步,选对技术方案是第一步。我见过不少团队,上来就选Debezium,结果业务场景其实只需要定时拉一下,白白增加了运维复杂度。也见过用触发器做全量同步的,生产库直接被拖垮。

今天咱们就把四种主流方案掰开揉碎聊一聊。基于日志、基于轮询、基于时间戳、基于触发器——每种我都踩过坑,也总结了一些实战经验。

核心观点:没有银弹。选型取决于你的数据一致性要求、数据库压力容忍度、以及团队运维能力。

一、基于日志的CDC(Debezium / Canal)

这是目前最主流的方案。说白了,就是让数据库把它的“操作日记”借给你看。MySQL的binlog、PostgreSQL的WAL、MongoDB的oplog,都是这个思路。

Debezium基于Kafka Connect,Canal是阿里开源的Java中间件。两者原理类似:伪装成数据库的从节点,实时接收日志流。

优点

  • 对源库无侵入——不需要改表结构,不需要加触发器,不产生额外查询。我在一个日活千万的电商系统上用过,binlog读取对主库性能影响几乎可以忽略。
  • 数据一致性高——日志记录了完整的事务上下文,能保证“要么全部成功,要么全部失败”。
  • 支持DDL变更——表结构改了也能感知到,这在做数据湖同步时特别有用。
  • 延迟极低——毫秒级,适合实时数仓和缓存同步。

缺点

  • 运维复杂——Debezium要搭Kafka集群,Canal要维护ZooKeeper。我见过一个小团队,为了跑CDC搭了6个组件,最后出问题都不知道从哪查起。
  • 日志保留期限制——MySQL的binlog默认只保留几天。如果下游挂了几天,日志被清理了,就得重新全量同步。
  • 版本兼容问题——数据库小版本升级可能导致解析器失效。我记得有一次MySQL从5.7升到8.0,binlog格式变了,Debezium直接报错。

我的建议:如果团队有专职的中间件运维人员,或者公司已经用了Kafka,优先选基于日志的方案。否则,先评估一下你能不能接受它的运维成本。

二、基于轮询的CDC

这是最“笨”但也是最通用的方法。定期执行SQL查询,把新增或变更的数据拉出来。比如每5秒跑一次 SELECT * FROM orders WHERE update_time > last_check。

优点

  • 实现简单——写个定时任务就行,不需要额外组件。我最早做数据同步时,一个Spring Scheduled加上JDBC就搞定了。
  • 数据库无关——不管MySQL、Oracle还是SQLite,只要支持SQL就能用。
  • 易于调试——出问题了直接看SQL执行结果,不像日志方案要翻Kafka消费位点。

缺点

  • 对源库有压力——每次轮询都是一次查询。如果表数据量大、轮询频率高,很容易把数据库打满。我曾经见过一个案例,轮询间隔设了1秒,结果生产库CPU直接飙到90%。
  • 无法捕获删除操作——除非你有软删除标记,否则物理删除的数据轮询是感知不到的。
  • 延迟取决于轮询间隔——设1秒就是1秒延迟,设1分钟就是1分钟。做不到真正的实时。
  • 数据重复问题——如果轮询期间数据被多次更新,可能会读到中间状态。需要做幂等处理。

避坑指南:我曾经在一个订单系统里用轮询做同步,没加ORDER BY和唯一约束,结果同一条订单被同步了三次,下游对账直接崩了。记住:轮询方案一定要配合去重机制和状态检查。

三、基于时间戳的CDC

这个方案和轮询有点像,但更“聪明”一点。它在表中增加一个时间戳字段(比如last_modified),每次更新时自动更新这个字段。同步时根据时间戳范围拉取数据。

优点

  • 实现简单——比轮询多一个字段而已。很多ORM框架(如MyBatis Plus)自带乐观锁和自动填充,用起来很方便。
  • 能捕获更新和新增——只要业务代码正确更新了时间戳,就能感知到。
  • 支持增量拉取——可以按时间窗口分批拉,避免一次拉太多。

缺点

  • 需要改表结构——必须加时间戳字段。对于老系统,改表可能涉及几十个上下游。
  • 依赖业务代码正确性——如果某个更新操作忘了设时间戳,数据就丢了。我在一个金融项目里遇到过,批量更新时用了UPDATE ... SET ... WHERE,但没更新last_modified,结果那批数据永远同步不过去。
  • 无法捕获删除——和轮询一样,物理删除感知不到。
  • 时间精度问题——如果同一毫秒内有多次更新,可能会漏掉。MySQL的datetime精度默认到秒,高并发场景下不够用。

我的建议:时间戳方案适合业务逻辑简单、表结构可控、且能接受秒级延迟的场景。如果你能保证每个写操作都正确更新时间戳,这个方案其实很稳。

四、基于触发器的CDC

在数据库表上创建触发器(Trigger),当发生INSERT、UPDATE、DELETE时,自动把变更记录写入一张日志表。同步程序再去读这张日志表。

优点

  • 能捕获所有操作——包括删除。触发器在事务内执行,不会漏掉。
  • 实时性高——触发器是同步触发的,数据一变,日志表立刻就有记录。
  • 不依赖业务代码——只要触发器建好了,不管业务怎么改,都能捕获到。

缺点

  • 对源库性能影响大——触发器是在事务内执行的。每次DML操作都要额外写一次日志表,事务时间变长,锁竞争加剧。我见过一个案例,加了触发器后,写入性能直接下降了40%。
  • 维护困难——触发器是数据库内部的逻辑,不好调试,不好版本管理。而且如果日志表结构变了,触发器也得跟着改。
  • 级联风险——触发器里如果写了复杂的逻辑,或者日志表出了故障,可能导致主表操作失败。
  • 数据库迁移麻烦——换数据库时,触发器语法可能不兼容,得重新写。

避坑指南:千万不要在生产核心表上直接加触发器做CDC。我见过最惨的一次,触发器里调了一个存储过程,存储过程又调了外部API,结果API超时,整个事务回滚,业务直接停了10分钟。触发器只适合做轻量级的日志记录,别在里面写复杂逻辑。

五、四种方案对比总表

维度 基于日志 基于轮询 基于时间戳 基于触发器
源库侵入性 无 有(查询压力) 有(改表结构) 有(触发器+日志表)
数据一致性 高(事务级) 中(可能重复/遗漏) 中(依赖时间戳) 高(事务内写入)
延迟 毫秒级 秒~分钟级 秒级 毫秒级
捕获删除 支持 不支持 不支持 支持
运维复杂度 高(Kafka/组件多) 低 低 中(触发器管理)
适用场景 实时数仓、缓存同步 低频同步、小数据量 可控业务、非核心系统 审计日志、小表同步

六、选型决策流程图

下面这张图是我自己总结的选型思路,帮你快速判断该用哪种方案。

CDC技术选型决策流程 开始选型 实时性要求高?(毫秒级) 是 有专职运维/已用Kafka? 是 基于日志(Debezium/Canal) 否 能改表结构? 是 基于时间戳 否 数据量小? 是 基于轮询 否 基于触发器(谨慎) 说明: • 基于日志:适合实时性高、有运维能力的团队,推荐Debezium + Kafka • 基于时间戳:适合能改表、业务可控的中小系统 • 基于轮询:适合数据量小、低频同步的简单场景 • 基于触发器:仅限审计日志或小表同步,生产核心表慎用

七、我的最终建议

如果你问我个人习惯怎么选,我会这么排优先级:

  1. 首选基于日志——只要团队能hold住Kafka,这是最省心的方案。数据一致性好,延迟低,对源库无侵入。Debezium社区活跃,文档齐全,遇到问题基本都能搜到答案。
  2. 次选基于时间戳——如果业务简单、表结构可控,而且能接受秒级延迟,时间戳方案性价比很高。不需要额外组件,出了问题也好排查。
  3. 轮询作为备胎——临时同步、数据量小、或者源库不支持日志的场景下用。但记得加去重和状态检查。
  4. 触发器尽量不用——除非你只是做审计日志,而且表很小、写入频率很低。否则,触发器带来的性能损失和维护成本,远大于它带来的便利。