2、主流CDC技术选型:基于日志的CDC、基于轮询的CDC、基于触发器的CDC

做CDC选型,说白了就是选一种「偷看」数据库变更的方式。我这些年接触过的项目,基本逃不出三种路子:日志、轮询、触发器。每种都有它的脾气,选错了后面运维会很难受。

咱们一个一个来看。

2.1 基于日志的CDC(Debezium、Canal)

这是目前最主流的方式。原理很简单——数据库自己就有事务日志(比如MySQL的binlog、PostgreSQL的WAL),CDC工具直接去读这些日志,解析出数据变更。

我个人习惯把这种方式叫做「旁路监听」。它不侵入业务表,不写触发器,不碰业务代码。数据库怎么写日志,它就怎么读。

核心优势: 对源库零侵入,延迟低(毫秒级),能捕获删除和旧值。

市面上两个代表项目:

  • Debezium:基于Kafka Connect生态,支持MySQL、PostgreSQL、MongoDB、Oracle等。我在一个金融项目里用它同步核心交易库到数仓,跑了两年没出过大问题。
  • Canal:阿里开源的MySQL binlog解析组件,专精MySQL。如果你只用MySQL,Canal更轻量,部署也简单。

举个例子,Debezium监听到一条INSERT,会吐出类似这样的JSON:

{
  "op": "c",           // c=create, u=update, d=delete
  "ts_ms": 1700000000000,
  "before": null,
  "after": {
    "id": 1001,
    "name": "张三",
    "balance": 5000.00
  },
  "source": {
    "db": "bank",
    "table": "accounts"
  }
}

嗯,这里要注意——日志格式因数据库而异。MySQL的binlog有ROW、STATEMENT、MIXED三种格式,CDC必须用ROW格式才能拿到完整数据。我曾经接手过一个项目,源库配的是STATEMENT格式,Debezium死活解析不出具体字段值,折腾了两天才发现是配置问题。

避坑指南: 使用基于日志的CDC前,务必确认数据库已开启日志归档,且日志保留时间足够长。我曾经因为binlog保留期只设了1天,周末管道挂了,周一恢复时日志已经过期,只能全量重刷。

2.2 基于轮询的CDC

轮询CDC,说白了就是「定时查表」。你在业务表上加一个时间戳字段或版本号字段,然后写个定时任务,每隔几秒或几分钟去查「最近有变化的数据」。

你想想看,这种方式有什么问题?

  • 延迟高:轮询间隔决定了延迟下限,做不到实时。
  • 无法捕获删除:行都没了,你查不到。
  • 对源库有压力:频繁的SELECT会抢业务库的连接和IO。

但它也有适用场景。我记得在一个ERP系统集成项目里,源库是Oracle 11g,版本太老不支持日志解析,业务方又不让加触发器。最后我们只能妥协——在核心表上加了个 last_modified 字段,每30秒轮询一次。虽然延迟能接受,但每次大促期间轮询线程一多,数据库CPU就往上蹿。

我的建议: 轮询CDC只适合以下情况——源库不支持日志解析、数据量小(百万级以内)、对实时性要求不高(分钟级)。如果条件允许,尽量别选这条路。

一个简单的轮询SQL示例:

-- 假设表结构中有 last_modified 字段
SELECT * FROM orders
WHERE last_modified > #{lastCheckTime}
ORDER BY last_modified ASC
LIMIT 1000;

这里有个坑——时间精度问题。如果业务在同一毫秒内更新了多条记录,轮询可能漏掉一部分。我一般建议用自增ID + 时间戳组合来做断点标记,更稳妥。

2.3 基于触发器的CDC

触发器CDC,就是在源库的表上创建INSERT、UPDATE、DELETE触发器,把变更记录写入一张专门的日志表。然后CDC工具再去读这张日志表。

这种方式听起来很直接,对吧?但我在实际项目中吃过它的亏。

先说优点:

  • 能捕获所有变更(包括删除和旧值)
  • 不依赖数据库日志格式
  • 兼容性广(任何支持触发器的数据库都能用)

再说缺点:

  • 性能开销大:每次DML操作都要额外写一条日志,事务变重。我在一个日活千万的电商订单库上试过,加了触发器后写入吞吐下降了约15%。
  • 维护成本高:表结构变更时,触发器也得同步更新。有一次DBA改了表字段类型,忘了更新触发器,结果日志表里全是NULL。
  • 单点故障风险:如果日志表满了或者挂了,业务表的写入也会被阻塞。
我曾经踩过的坑: 在一个财务系统里用了触发器CDC,结果某天日志表的自增ID达到上限,所有业务写入全部报错。从那以后,我对触发器方案就格外谨慎。

一个典型的触发器示例(MySQL):

CREATE TRIGGER trg_orders_after_update
AFTER UPDATE ON orders
FOR EACH ROW
BEGIN
  INSERT INTO cdc_orders_log(
    operation, table_name, record_id,
    old_data, new_data, create_time
  ) VALUES (
    'UPDATE', 'orders', NEW.id,
    JSON_OBJECT('status', OLD.status, 'amount', OLD.amount),
    JSON_OBJECT('status', NEW.status, 'amount', NEW.amount),
    NOW()
  );
END;

2.4 三种方案对比

我把它们放在一起对比,你一眼就能看出差异:

对比维度 基于日志 基于轮询 基于触发器
延迟 毫秒级 秒~分钟级 毫秒级
源库侵入性 无 低(仅查询) 高(写触发器)
捕获删除 ✅ 支持 ❌ 不支持 ✅ 支持
捕获旧值 ✅ 支持 ❌ 不支持 ✅ 支持
对源库性能影响 极低 中等(取决于轮询频率) 较高
维护复杂度 低 低 高
适用场景 生产环境、实时场景 老旧系统、非实时 小规模、无日志解析支持

2.5 选型决策逻辑

我一般按这个思路来选:

  1. 先看源库是否支持日志解析。MySQL 5.7+、PostgreSQL 9.4+、Oracle 12c+ 基本都支持。能用日志就别想别的。
  2. 再看实时性要求。秒级以内必须上日志CDC;分钟级可以接受的话,轮询也能凑合。
  3. 最后看运维能力。日志CDC需要维护Kafka或类似的消息中间件,团队如果没经验,小项目可以先用触发器过渡。

下面这张图能帮你快速定位:

CDC 技术选型决策流程 开始选型 源库支持日志解析? 是 实时性要求 < 1秒? 是 ✅ 基于日志CDC(Debezium/Canal) 否 轮询CDC(可接受) 否 数据量 < 100万? 是 触发器CDC(可考虑) 否 升级数据库或换方案 选型建议 • 生产环境首选基于日志CDC,延迟低、侵入小、维护简单 • 老旧系统或临时方案可考虑轮询或触发器,但需评估性能影响 • 触发器方案尽量避开高并发写入场景
我的个人经验: 如果团队刚接触CDC,我建议从Debezium + Kafka入手。生态成熟,社区活跃,遇到问题基本都能搜到答案。别一上来就搞自研或者冷门工具,坑太多。

总结一下:基于日志的CDC是首选,基于轮询的是备选,基于触发器的是最后的选择。选型时别只看功能列表,还要考虑你团队的运维能力和源库的实际情况。毕竟,再好的技术方案,落不了地也是白搭。