2、CDC核心原理:日志捕获机制、增量数据识别、数据变更事件

CDC这东西,说白了就是让数据库自己告诉我们它变了什么。而不是我们每隔几秒去问它“你变了没?”。我个人习惯把CDC的核心拆成三个环节:怎么抓到变化、怎么识别哪些是增量、怎么把变化包装成事件。这三个环节搞清楚了,选型就不会抓瞎。

2.1 日志捕获机制:数据库的“黑匣子”

几乎所有主流数据库都会记录事务日志。MySQL有binlog,PostgreSQL有WAL,Oracle有Redo Log。这些日志原本是为了崩溃恢复和主从复制用的。但你想,主从复制不就是最原始的CDC吗?

我在项目中遇到过不少团队,想自己写脚本去查表的LAST_MODIFIED时间戳来做增量。嗯,小数据量还行,一旦上了千万级,性能直接崩。而且你根本捕获不到删除操作——除非你用软删除。

所以,真正的CDC一定要走日志。日志捕获机制主要有两种模式:

模式 原理 典型代表 我踩过的坑
基于日志解析 直接读取数据库的事务日志文件,解析成结构化数据 Debezium、Canal、Maxwell binlog格式没配成ROW,解析出来全是SQL语句,根本没法用
基于触发器/API 在表上建触发器,或者调用数据库的变更跟踪API SQL Server Change Tracking、Oracle GoldenGate 触发器对源库性能影响很大,生产环境慎用

我个人更倾向于基于日志解析的方式。因为它对源库几乎零侵入。你想想,日志本来就在写,我们只是多了一个消费者而已。

核心原则:日志捕获必须做到“读日志不写日志”,不能反过来往源库写任何东西。否则CDC就变成了另一个压力源。

2.2 增量数据识别:从日志流里捞出“干净”的变化

日志文件是连续的、二进制的、混杂的。里面有事务开始、事务提交、数据变更、检查点、心跳……我们要做的,就是从这堆“噪音”里精准捞出每一行数据的每一次变更。

增量识别的关键步骤,我总结为三步:

  1. 定位起始点:第一次同步需要知道从哪个日志位置开始。通常是全量快照完成时的日志偏移量。
  2. 过滤无关操作:只保留INSERT、UPDATE、DELETE。DDL操作、心跳包、检查点全部跳过。
  3. 还原变更前/后镜像:对于UPDATE,日志里通常只记录变更后的值。但很多场景下我们需要“变更前”的值来做比对或回滚。

这里有个坑。我曾经遇到一个项目,MySQL的binlog格式配的是STATEMENT,结果解析出来的全是UPDATE t SET name='xxx' WHERE id IN (...)这种SQL片段。你根本不知道具体哪几行变了,更拿不到变更前的值。后来我强制要求所有CDC场景下,binlog格式必须设为ROW。这是铁律。

避坑指南:我曾经因为binlog格式没配对,导致增量数据解析出来全是乱码。排查了整整两天才发现是binlog_row_image设成了MINIMAL,只记录了变更列的镜像,主键以外的字段全丢了。生产环境建议用FULL。

2.3 数据变更事件:把日志行变成业务能懂的消息

日志解析出来的是二进制字节流。业务系统看不懂这些。我们需要把它包装成结构化的数据变更事件。

一个标准的数据变更事件,至少包含以下字段:

字段 说明 示例
op 操作类型:c=创建,u=更新,d=删除,r=快照读取 u
ts_ms 事件发生的时间戳(毫秒) 1698123456789
before 变更前的数据行(DELETE和UPDATE有值) {"id":1,"name":"旧值"}
after 变更后的数据行(INSERT和UPDATE有值) {"id":1,"name":"新值"}
source 元数据:库名、表名、日志偏移量、线程ID等 {"db":"shop","table":"orders"}

Debezium的Kafka Connect Connector就是按这个格式输出的。我个人觉得这个设计很优雅——它把数据库底层的日志语义,映射成了消息队列里的一条条JSON消息。下游的消费者不用关心你用的是MySQL还是PostgreSQL,统一消费事件就行。

小技巧:如果你用Kafka做事件总线,建议把topic按库名.表名来命名,比如shop.orders。这样下游可以根据topic直接订阅关心的表,不用在消息体里再过滤一遍。

2.4 核心流程总览:一张图看懂CDC

下面这张图,是我自己画的一个CDC核心流程。从源库的日志文件开始,到最终投递到消息队列,每一步都对应我们刚才讲的内容。

CDC增量同步核心流程 CDC增量同步核心流程 源数据库 事务日志 binlog / WAL / Redo Log 日志解析引擎 Debezium / Canal 增量识别 过滤 + 镜像还原 数据变更事件 JSON / Avro 格式 消息队列 Kafka / Pulsar 下游消费者 数仓 / 缓存 / 搜索 关键说明: 1. 日志解析引擎从源库的事务日志中读取原始变更数据。 2. 增量识别模块过滤掉DDL、心跳等无关操作,还原变更前/后镜像。 3. 最终输出为结构化的数据变更事件,投递到消息队列供下游消费。

你看,整个链路其实不复杂。但每个环节都有细节。日志解析要处理断点续传,增量识别要处理大事务拆分,变更事件要考虑schema演进。这些我们后面会展开讲。

嗯,这一章先到这里。核心就是记住:CDC的本质是把数据库的日志,翻译成业务系统能消费的事件流。后面的章节,我们会围绕这个流程,一个一个环节深入进去。