喜欢把枯燥的技术文档变成"手把手教程",不讲空话,只讲怎么连、怎么写、怎么优化。
如果你在做实时数仓、跨库同步、或者数据中台,CDC 这个概念你一定绕不开。
Change Data Capture------变更数据捕获。简单说就是:源库的数据变了,你能实时知道变了什么,同步到目标端。
听起来简单,但方案选型时坑不少。定时轮询(ETL)延迟高、对源库压力大;日志解析方案性能好,但搭建复杂、运维门槛高。中间还有时间戳方案、触发器方案,各自有适用场景。
今天把 CDC 的主流方案拆开讲清楚,每种方案的原理、优缺点、搭建方式、踩过的坑。如果你正在选型或者正在搭 CDC 管道,这篇应该能帮你少踩几个坑。
CDC 是什么:为什么不用定时轮询
先解决一个基础问题:既然我要的是"源库变了,目标端同步",为什么不能定时查一下?
定时轮询(ETL)的逻辑是:每隔 N 分钟执行一次 SELECT * FROM table WHERE update_time > last_sync_time。
问题有三个:
1. 延迟是固定的。 你设 5 分钟轮询,延迟至少 5 分钟。设 1 分钟,延迟至少 1 分钟。对于实时数仓或者实时风控,这个延迟受不了。
2. 对源库有压力。 每次轮询都是一次全表扫描或者范围查询。表大了之后,这条 SQL 本身就占资源。频率高了,跟业务查询抢资源。
3. 漏数据。 如果一条数据在两次轮询之间被插入又被删除,轮询就捕获不到。或者时间戳字段没更新,变更就漏了。
CDC 的思路不同:直接监听数据库的变更日志,变更发生的那一刻就知道。不是"定时去查",是"实时监听"。
CDC 的三种实现方式:时间戳、触发器、日志解析
先快速过一遍 CDC 的三种实现路径,再重点讲日志解析。
方式一:时间戳方案
每张表加 update_time 字段,定期扫描大于上次同步时间的记录。
优点 :实现最简单,不需要额外组件。
缺点 :就是上面说的那三个问题------延迟固定、有压力、漏数据。
适用场景:数据量小、延迟要求不高的离线同步。
方式二:触发器方案
在源库的每张表上建触发器,INSERT/UPDATE/DELETE 时把变更记录到一张中间表,CDC 程序读中间表。
优点 :能捕获所有变更,不漏数据。
缺点 :触发器是同步执行的,每次写入都要多写一条变更记录,源库写入性能直接受影响。表多了之后,触发器管理也麻烦。
适用场景:源库写入压力不大、需要精确捕获变更的小规模场景。
方式三:日志解析方案(重点)
直接解析数据库的事务日志------MySQL 的 binlog、PostgreSQL/KES 的 WAL------从中提取变更事件。
优点 :对源库几乎零侵入(只需要开启日志)、延迟低(毫秒到秒级)、不漏数据。
缺点 :搭建复杂度高,需要理解日志格式、处理乱序/重复、维护解析程序。
适用场景:实时数仓、跨库同步、数据中台------对延迟和数据完整性要求高的场景。
下面重点讲日志解析方案。
MySQL binlog 解析:Canal / Debezium / Maxwell
MySQL 生态里,binlog 解析是主流 CDC 方案。binlog 记录了数据库的所有变更事件,格式有 STATEMENT、ROW、MIXED 三种。做 CDC 必须用 ROW 格式,因为需要记录每条数据的具体变更内容。
Canal
阿里开源的 binlog 解析工具,国内用得最多。
arduino
MySQL(开启 binlog ROW 格式)
→ Canal Server(伪装成 MySQL slave,拉取 binlog)
→ Canal Client(消费变更事件)
→ Kafka / 直写目标库
Canal 的核心思路是把自己伪装成一个 MySQL slave。MySQL 的 binlog 本来就是给 slave 同步用的,Canal 连上去,MySQL 就把它当 slave,把 binlog 推过来。Canal 解析后,通过 Client 消费。
优点 :国内生态成熟、社区活跃、中文文档多。支持 HA 部署。
缺点:只支持 MySQL。如果源库有 PostgreSQL,得换其他方案。
Debezium
Red Hat 开源的 CDC 平台,支持多种数据库。
markdown
MySQL/PostgreSQL/KES(开启日志)
→ Debezium Connector(Kafka Connect 插件,解析日志)
→ Kafka(变更事件按 topic 分发)
→ Kafka Consumer(消费写入目标端)
Debezium 走的是 Kafka Connect 生态。每个数据库一个 Connector,Connector 解析日志后把变更事件发到 Kafka topic。消费者从 topic 取数据写到目标端。
优点 :多数据库支持(MySQL、PostgreSQL、SQL Server、Oracle 等)、和 Kafka 生态无缝集成、Schema 变更自动处理。
缺点:依赖 Kafka,架构复杂度上了一个台阶。
Maxwell
轻量级的 binlog 解析工具,输出 JSON 格式。
javascript
MySQL(binlog ROW)
→ Maxwell(读取 binlog,输出 JSON 到 Kafka/Kinesis/文件)
→ 下游消费
优点 :部署简单、配置少、直接输出 JSON。
缺点:功能相对单一,不支持 HA,社区活跃度不如 Canal 和 Debezium。
对比总结
| 维度 | Canal | Debezium | Maxwell |
|---|---|---|---|
| 支持数据库 | 仅 MySQL | MySQL/PG/SQL Server/Oracle 等 | 仅 MySQL |
| 部署复杂度 | 中(需部署 Server + Client) | 高(需 Kafka + Kafka Connect) | 低(单进程) |
| HA 支持 | 支持 | 支持(依赖 Kafka) | 不支持 |
| Schema 变更 | 需手动处理 | 自动处理 | 需手动处理 |
| 社区活跃度 | 高(国内) | 高(国际) | 中低 |
| 适用场景 | 国内 MySQL 生态 | 多数据库 + Kafka 生态 | 轻量级快速接入 |
PostgreSQL WAL 解析:wal2json / pgoutput / Debezium
PostgreSQL 的 CDC 靠的是 WAL(Write-Ahead Log)逻辑解码。
WAL 是 PostgreSQL 的内部日志,记录所有数据变更。逻辑解码把 WAL 中的物理变更记录解析成逻辑变更(INSERT/UPDATE/DELETE 的行级数据)。
wal2json
PostgreSQL 的逻辑解码插件,把 WAL 解析成 JSON 格式输出。
ini
PostgreSQL(开启 wal_level=logical)
→ wal2json 插件(逻辑解码,输出 JSON)
→ 消费程序(读取 JSON 变更事件)
→ 目标端
配置步骤:
-
修改
postgresql.conf:iniwal_level = logical max_replication_slots = 4 max_wal_senders = 4 -
重启数据库
-
创建 replication slot:
arduinoSELECT pg_create_logical_replication_slot('my_cdc_slot', 'wal2json'); -
消费变更:
sqlSELECT * FROM pg_logical_slot_get_changes('my_cdc_slot', NULL, NULL);
优点 :轻量、纯插件、输出格式清晰。
缺点:需要自己写消费程序处理 JSON、replication slot 如果消费不及时会导致 WAL 堆积。
pgoutput
PostgreSQL 10+ 内置的逻辑解码插件,不需要额外安装。
ini
PostgreSQL 10+(wal_level=logical)
→ pgoutput(内置逻辑解码)
→ 消费程序(如 Debezium PG Connector)
→ Kafka / 目标端
优点 :内置、不需要额外安装插件、和 Debezium 集成好。
缺点:输出格式是 protobuf,需要 Debezium 等工具解析。
Debezium PostgreSQL Connector
和 MySQL Connector 类似,只是源端换成了 PostgreSQL/KES 的 WAL 解析。
ini
PostgreSQL(wal_level=logical)
→ Debezium PG Connector(用 pgoutput 解析 WAL)
→ Kafka
→ 下游消费
和 MySQL Connector 类似,只是源端换成了 PostgreSQL 的 WAL 解析。配置和标准 PostgreSQL 完全一致,关键点是确保 wal_level=logical 和 replication slot 数量够用。Debezium 的 PG Connector 对 PostgreSQL 各个版本兼容性都很好。
CDC 管道搭建:完整链路
一个完整的 CDC 数据管道,链路是这样的:
markdown
源数据库
→ 日志解析(Canal / Debezium / wal2json)
→ 消息中间件(Kafka / Pulsar,可选)
→ 数据转换(格式转换、字段映射、过滤)
→ 目标数据库(写入)
→ 监控告警(延迟、积压、错误率)
几个关键配置点:
1. 源端日志配置
MySQL:binlog_format=ROW,binlog_row_image=FULL(记录完整的前后镜像)。
PostgreSQL:wal_level=logical,max_replication_slots 和 max_wal_senders 至少设为 4。
2. 复制槽管理
PostgreSQL/KES 的逻辑复制 slot 如果消费者断连,WAL 会持续堆积直到磁盘满。必须监控 slot 的 lag:
vbnet
SELECT slot_name, active,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes
FROM pg_replication_slots;
如果 lag_bytes 持续增长,说明消费者断了,需要排查。
3. 乱序和重复处理
CDC 管道在异常情况下可能出现乱序或重复消费。目标端需要做幂等写入:
- 用主键做 UPSERT(
INSERT ... ON CONFLICT UPDATE) - 或者在消息里带序列号,目标端按序列号去重
4. DDL 变更处理
表结构变更(加字段、改类型)是 CDC 管道最容易出问题的地方。binlog/WAL 里有 DDL 事件,但下游消费程序不一定能处理。
建议:DDL 变更时,暂停 CDC 管道、同步目标端 schema、再恢复管道。或者用 Debezium 的 Schema Registry 自动处理。
常见问题和排查
问题一:同步延迟越来越大
排查步骤:
- 看源端日志产生速度 :MySQL
SHOW MASTER STATUS看 binlog 位置变化,PostgreSQLpg_current_wal_lsn()看 WAL 增长 - 看解析程序消费速度:解析程序的 lag 指标是否在增长
- 看目标端写入速度:目标库是否有写入瓶颈(锁等待、索引过多、磁盘 I/O 满)
- 看消息中间件积压:Kafka topic 的 consumer lag
问题二:数据不一致
排查步骤:
- 抽取关键表抽样比对:源端和目标端按主键对比几条数据
- 检查 replication slot 是否有跳过 :
pg_replication_slots的 active 状态 - 检查是否有 DDL 变更没同步:源端加了字段,目标端没加,写入会报错
- 检查幂等写入逻辑:是否有重复消费导致数据翻倍
问题三:源库性能受影响
CDC 对源库的影响主要在日志写入。binlog/WAL 开启后,每次写入多一步日志记录。
优化:
- binlog/WAL 放独立磁盘,不和业务数据盘争 I/O
- 适当调整日志文件大小和轮转策略
- 过滤不需要同步的表(临时表、日志表)
方案选型决策框架
| 你的场景 | 推荐方案 | 理由 |
|---|---|---|
| 源库只有 MySQL,团队熟悉国内生态 | Canal + Kafka | 成熟、文档多、中文社区支持好 |
| 源库有多种数据库(MySQL + PG + Oracle) | Debezium + Kafka | 统一平台、多源支持 |
| 源库是 PostgreSQL,想要轻量方案 | wal2json + 自写消费程序 | 配置简单、无额外依赖 |
| 源库是 PostgreSQL,需要和 Kafka 集成 | Debezium PG Connector | 开箱即用,和 Kafka 生态无缝集成 |
| 数据量小、延迟要求不高 | 时间戳轮询 | 最简单、零额外组件 |
| 需要秒级延迟、高可靠性 | 日志解析 + Kafka + 幂等写入 | 工业级方案,生产验证充分 |
CDC 管道上线前检查清单
- 源库日志格式正确(MySQL ROW / KES logical)
- replication slot 数量足够
- WAL/binlog 磁盘空间监控已配置
- 目标端幂等写入逻辑已实现
- DDL 变更处理流程已确认
- 不需要同步的表已过滤
- 延迟监控告警已配置(超过 1 分钟告警)
- 数据一致性抽样验证已跑过
- 故障恢复流程已测试(消费断连、重启、追赶)
- 源库性能基线已采集(开启 CDC 前后的对比)
总结
CDC 不是"选个工具装上就行"的事。
日志解析是核心方案,但选 Canal、Debezium 还是 wal2json,取决于你的源库类型、团队技术栈、架构复杂度接受度。MySQL 生态选 Canal 最省心,多数据库混选 Debezium,PostgreSQL/KES 轻量场景用 wal2json 就够了。
管道搭建之后,重点监控三件事:复制槽 lag(防 WAL 堆积)、消费延迟(防数据滞后)、数据一致性(防丢数据)。DDL 变更是最大隐患,必须有处理流程。
CDC 是实时数据链路的基础设施。地基打好了,上面的实时数仓、数据中台、跨库同步才能稳。
后续我会继续分享数据库版本升级实战、云数据库 vs 自建这些话题,跟着我一篇篇学,数据库这块就没问题了。
有问题评论区见。
喜欢把枯燥的技术文档变成"手把手教程"。关注我,数据库这块我们一起搞定。