增量数据采集:CDC技术原理与工具选型
数据湖仓的增量处理,第一步就是「怎么把变化的数据捞出来」。
CDC(Change Data Capture,变更数据捕获)就是干这个的。说白了,它能让下游实时感知到数据库里哪行数据被改了、被删了、被插入了。没有CDC,你只能全量跑批,一天一次,延迟高得吓人。
我最早做数仓时,用的还是时间戳+定时扫描的方式。后来接触了日志解析,才发现以前走了不少弯路。今天我把三种主流CDC原理和几个常用工具掰开揉碎讲清楚。
核心观点:CDC不是银弹,选型要看业务对延迟、一致性、侵入性的容忍度。日志解析型最优雅,但复杂度也最高。
三种CDC原理:日志解析、时间戳、触发器
这三种方式,我按「推荐程度」从高到低来讲。
1. 日志解析(Log-based CDC)
这是目前最主流的方式。数据库的每次写入、更新、删除,都会记录在事务日志里(比如MySQL的binlog、PostgreSQL的WAL、Oracle的Redo Log)。
CDC工具直接读取这些日志,解析出变更事件,然后推送给下游。
优点很明显:
- 对业务代码零侵入——你不需要改表结构、加字段
- 延迟极低——日志一写,立刻就能读到
- 能捕获所有变更,包括删除操作
缺点呢?
- 日志格式各数据库不同,解析逻辑复杂
- 日志有保留期限,如果消费不及时可能丢失数据
- 需要数据库开启相关配置(比如MySQL要开binlog且设为ROW模式)
我的经验:我在一个金融项目中,就因为binlog保留时间只设了24小时,结果下游Kafka消费集群挂了半天,恢复后日志已经过期。从那以后,我习惯把binlog保留期至少设到72小时,并且加监控报警。
2. 时间戳(Timestamp-based CDC)
这是最朴素的方式。在源表上加一个last_modified或update_time字段,每次更新时由业务代码或数据库触发器更新这个字段。下游定时扫描:WHERE update_time > last_check_time。
优点:
- 实现简单,几乎任何数据库都能做
- 不需要额外配置日志
缺点:
- 无法捕获删除操作(除非用软删除)
- 时间精度问题——同一毫秒内的多条变更可能漏掉
- 对业务代码有侵入,必须保证每个写操作都更新时间戳
- 扫描频率决定了延迟,频率高了又影响数据库性能
注意:时间戳方式在分布式系统里尤其容易出问题。各服务器时钟不同步,可能导致数据漏采或重复。我曾经踩过这个坑,后来强制所有应用服务器用NTP同步,并且把时间戳字段改成数据库服务器本地时间。
3. 触发器(Trigger-based CDC)
在源表上创建触发器(INSERT/UPDATE/DELETE),当数据变更时,触发器把变更记录写入一张专门的日志表。CDC工具再读取这张日志表。
优点:
- 能捕获所有变更,包括删除
- 不依赖数据库日志格式
缺点:
- 对源库性能影响大——每次DML操作都要额外写一次日志表
- 触发器逻辑维护成本高,改表结构时容易出问题
- 如果日志表没有清理机制,会无限膨胀
我个人不太推荐触发器方式,除非你用的是那种不支持日志解析的老旧数据库,而且数据量很小。
三种原理对比
| 维度 | 日志解析 | 时间戳 | 触发器 |
|---|---|---|---|
| 业务侵入性 | 无 | 中(需加字段) | 低(需加触发器) |
| 延迟 | 秒级/毫秒级 | 取决于扫描间隔 | 秒级 |
| 捕获删除 | 支持 | 不支持(需软删除) | 支持 |
| 数据库性能影响 | 低 | 中(扫描压力) | 高 |
| 实现复杂度 | 高 | 低 | 中 |
| 典型场景 | 实时数仓、数据湖 | 离线批处理、小数据量 | 老旧数据库、无日志场景 |
常见CDC工具对比:Debezium、Canal、Maxwell
工具选型,我建议先看你的技术栈。Java生态用Canal或Debezium都行,但如果你已经在用Kafka,Debezium几乎是首选。
1. Debezium
这是目前最火的CDC工具,基于Kafka Connect架构。它支持MySQL、PostgreSQL、MongoDB、Oracle、SQL Server等多种数据库。
核心特点:
- 原生集成Kafka,变更事件直接写入Kafka Topic
- 支持快照+增量,首次启动会做一次全量快照,然后无缝切换到增量
- 提供变更历史Topic,可以回溯任意时间点的数据状态
- 社区活跃,文档完善
我去年帮一家电商公司搭实时数仓,用的就是Debezium + Kafka + Flink。MySQL的binlog变更,秒级就能进入Kafka,然后Flink做ETL写入Iceberg表。整个链路延迟不到3秒。
Debezium配置示例(MySQL Connector):
{
"name": "inventory-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "192.168.1.100",
"database.port": "3306",
"database.user": "debezium",
"database.password": "dbz_pass",
"database.server.id": "184054",
"database.server.name": "dbserver1",
"database.include.list": "inventory",
"table.include.list": "inventory.orders,inventory.customers",
"database.history.kafka.bootstrap.servers": "kafka:9092",
"database.history.kafka.topic": "schema-changes.inventory",
"include.schema.changes": "true",
"snapshot.mode": "initial"
}
}
2. Canal
Canal是阿里巴巴开源的,专门针对MySQL binlog解析。它伪装成MySQL的从库,向主库请求binlog。
核心特点:
- 纯Java实现,部署简单
- 支持直接投递到Kafka、RocketMQ、或者TCP直连
- 阿里巴巴内部大规模验证过,稳定性高
- 但只支持MySQL(以及兼容MySQL协议的数据库)
适用场景:如果你的数据源只有MySQL,而且团队对Java生态熟悉,Canal是个轻量好用的选择。
避坑指南:我曾经遇到Canal在解析大事务binlog时内存飙升的问题。原因是Canal默认会把整个事务的binlog都加载到内存。解决方案是调整canal.instance.memory.buffer.size和canal.instance.memory.buffer.memunit参数,或者开启transaction.flat模式让Canal逐条解析。
3. Maxwell
Maxwell也是一个MySQL CDC工具,由Zendesk开源。它的设计理念是「简单」——输出格式就是JSON,直接写入Kafka、Kinesis、RabbitMQ等。
核心特点:
- 输出格式极其简洁,就是一个JSON字符串
- 支持
bootstrap模式做全量同步 - 支持分区键自定义
- 同样只支持MySQL
Maxwell输出示例:
{
"database": "inventory",
"table": "orders",
"type": "insert",
"ts": 1696000000,
"data": {
"id": 1001,
"customer_id": 500,
"total": 299.00,
"status": "pending"
}
}
Maxwell的优点是轻量,一个JAR包就能跑。但功能上比Debezium弱一些,比如不支持schema evolution的细粒度控制。
三款工具横向对比
| 维度 | Debezium | Canal | Maxwell |
|---|---|---|---|
| 支持数据库 | MySQL、PG、MongoDB、Oracle、SQL Server等 | MySQL | MySQL |
| 架构 | Kafka Connect | 独立Server | 独立Server |
| 输出目标 | Kafka(原生) | Kafka、RocketMQ、TCP | Kafka、Kinesis、RabbitMQ、文件 |
| 全量快照 | 支持(snapshot mode) | 需额外开发 | 支持(bootstrap) |
| Schema变更 | 支持(schema history topic) | 有限支持 | 有限支持 |
| 社区活跃度 | 高 | 中 | 低 |
| 学习曲线 | 中(需理解Kafka Connect) | 低 | 低 |
CDC在数据湖仓中的整体流程
下面这张图,是我在实际项目中常用的CDC数据流向。你可以看到,从源库到数据湖,中间经过了日志采集、消息队列、流处理、湖存储几个环节。
选型建议
说了这么多,到底怎么选?我给出几个实际场景的推荐:
- 场景一:多数据库源(MySQL + PG + MongoDB),已有Kafka集群 → Debezium
- 场景二:纯MySQL,团队Java技术栈,不想引入Kafka Connect → Canal
- 场景三:纯MySQL,数据量不大,想要极简部署 → Maxwell
- 场景四:老旧数据库不支持日志解析 → 考虑时间戳或触发器方案
最后提醒一句:不管选哪个工具,一定要在生产环境做好监控。CDC链路一旦断了,数据延迟会越积越大。我习惯给每个CDC Connector加一个延迟监控,超过5分钟就报警。别问我为什么——经历过一次48小时的数据断层,你就懂了。