CDC 模式 = 遗留数据库的事件化
解决什么问题:遗留数据库的事件流化—— CDC(Change Data Capture)从 DB 事务日志读取变更,避免应用层双写。
业务问题
真实场景
场景 1:从单体迁移到事件驱动
问题:
- 现有 MySQL 数据库
- 业务逻辑在应用层
- 想增加事件流供下游消费
反模式(应用层双写):
def update_order(self, order):
self.db.update(order) # 写 DB
self.event_bus.publish(...) # 发事件
# 问题:DB 写成功但事件失败 → 不一致
正模式(CDC):
def update_order(self, order):
self.db.update(order) # 只写 DB
# Debezium 监听 binlog → 自动发事件
# 问题解决:DB 是 SSOT,事件自动生成Bellemare Ch 4 原话:
"Change Data Capture... A changelog is a record of all changes made to the data of the state store... As a permanent copy of the state maintained outside of the microservice instance, the changelog can be used to rebuild state."
失败模式
| 失败模式 | 后果 |
|---|---|
| 应用层双写 | DB 与事件不一致 |
| 轮询查询 | 高延迟、低效 |
| 触发器 | 性能差、难维护 |
控制论视角
Ch 15:"测量是被控对象状态反馈给控制律的关键环节" + Ch 4 §4.6:"单一真理源"
CDC = 数据库的"测量机制"
- DB = 被控对象(SSOT)
- binlog = 测量信号(不可变、连续)
- 事件流 = 测量的"投影"(物化)
优势:
- 不破坏被控对象
- 测量是连续的、不可丢失的
- 测量的延迟可监控核心概念
CDC 4 模式对比
| 模式 | 实时性 | 侵入性 | 性能 | 适用 |
|---|---|---|---|---|
| CDC (binlog) | 实时(ms) | 低 | 高 | ★ 推荐 |
| Trigger | 实时 | 高 | 低 | 简单场景 |
| Timestamp Polling | 准实时 | 中 | 中 | 无 binlog |
| Dual Write | 实时 | 高 | 高 | ★ 反模式 |
CDC 工具对比
| 工具 | 数据库 | 特点 |
|---|---|---|
| Debezium | MySQL/PostgreSQL/MongoDB | 开源、Apache 2.0 |
| AWS DMS | 多 | 托管、简单 |
| Oracle GoldenGate | Oracle | 商业、强大 |
| Maxwell | MySQL | 轻量 |
CDC 工作流
DB (MySQL)
↓ binlog
Debezium Connector
↓ 转换为事件
Kafka Topic
↓
下游服务Schema 演进与 CDC
挑战:DB schema 改变时 CDC 怎么处理?
方案:
- Debezium 支持 schema history topic
- 记录所有 DDL 变更
- Consumer 可同步 schema与 IDDD 集成
模式 1:DB 是聚合存储 + CDC 发事件
应用写聚合 → DB
CDC 监听 → 发聚合事件
下游消费 → 物化
模式 2:DB 是聚合存储 + 应用发领域事件
应用写聚合 → DB
应用发事件(与 DB 同一事务)
下游消费模式 1 是首选(避免双写)。
工程实践
实战 1:Debezium + Kafka + MySQL
yaml
# debezium-connector-config.json
{
"name": "orders-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "mysql",
"database.port": "3306",
"database.user": "debezium",
"database.password": "...",
"database.server.id": "184054",
"database.server.name": "orders",
"table.include.list": "orders_db.orders,orders_db.items",
"tombstones.on.delete": "true",
"transforms": "route",
"transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
"transforms.route.regex": "([^.]+)\\.([^.]+)\\.([^.]+)",
"transforms.route.replacement": "$3"
}
}实战 2:避免常见错误
错误 1:把 CDC 当"万能解"
- CDC 只解决"数据释放"
- 不解决业务逻辑迁移
错误 2:忽略 schema 演进
- DDL 改变 → Debezium schema history
- 必须配置
错误 3:下游强耦合到 DB schema
- CDC 事件应转换为业务事件
- 不应暴露 DB 内部结构实战 3:监控 CDC 健康
关键指标:
- binlog 消费 lag
- 事件发送速率
- 失败事件数(DLQ)
- Schema 兼容性状态反模式 vs 正模式
| 反模式 | 正模式 |
|---|---|
| 应用层双写(DB + 事件) | CDC 单写 |
| 触发器发事件 | CDC binlog 监听 |
| 轮询查询 | CDC 实时 |
| 暴露 DB schema | 转换为业务事件 |
性能指标
| 指标 | 衡量 | 健康值 |
|---|---|---|
| CDC 延迟 | DB 变更 → 事件发出 | < 1s |
| 事件丢失率 | 丢失事件数 / 总事件 | 0 |
| 下游事件覆盖率 | 下游接收事件的比例 | > 99% |
| Schema 兼容性 | schema 演进成功率 | > 99% |
边界判断
何时用 CDC(★ 推荐)
- 遗留系统迁移到事件驱动
- 需要实时数据流
- 不想改动现有应用
- 多下游需要同一数据
何时不用
- 新建项目(直接用事件)
- 数据库不支持 binlog
- 性能瓶颈在 DB(CDC 增加负载)
何时升级
| 当前 | 升级到 | 触发条件 |
|---|---|---|
| 轮询查询 | CDC | 实时性需求 |
| Trigger | CDC | 性能/可靠性 |
| Dual Write | CDC | 一致性问题 |
关键洞察
- CDC = 数据库的"自然日志"——binlog/WAL 已经是 SSOT 的测量信号。
- 避免应用层双写——DB 和事件不一致是分布式系统最大痛点。
- DB 是 SSOT,事件是投影——这不是冗余,是不同用途(读写分离)。
- Schema 演进必须配套——DDL 改变 → CDC 也要适配。
- 与 cybernetics 同构——Ch 15 测量的不可丢失性 = CDC 的 at-least-once 语义。