#DevOps

CDC 变更数据捕获:实时数据同步

每次改数据手动触发下游同步?CDC 自动捕获数据库变更,实时推给下游(搜索/缓存/数仓)。本文讲清 CDC 的原理(读 WAL 日志)、工具(Debezium)、适用场景(同步 ES/缓存/数仓/微服务事件)、以及它比"双写/定时轮询"强在哪。

本文要点

  • 主题聚焦:DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年3月30日·更新于 2026年7月23日

CDC 变更数据捕获:实时数据同步

一个很常见的场景:你有一张 orders 表写在 Postgres 里,但搜索要用 Elasticsearch,缓存要用 Redis,数仓要进 ClickHouse,还要给履约微服务发个事件。

每次写库之后,你怎么办?

最直觉的写法是——在业务代码里"双写":先 INSERT INTO orders ...,再 index into es,再 XADD 到 Redis Stream,再发个 Kafka 消息。看起来清晰,实际上是个定时炸弹。本文讲清楚为什么 CDC(Change Data Capture,变更数据捕获)是这类问题的更优解,以及它到底怎么工作、什么时候值得上。

一、CDC 解决什么:让数据库变更"自动"流向下游

CDC 的核心一句话:数据库自己知道每次变更,我们把它捕获下来,推给所有需要的下游。

不再由应用代码负责"通知"每个下游,而是有一个组件趴在数据库旁边,把 insert / update / delete 事件实时拎出来,广播出去。下游谁需要谁订阅。

text
                   ┌──────────┐
                   │ Postgres │ (WAL 日志在不断写入)
                   └────┬─────┘
                        │ CDC 组件读取日志
                        ▼
                   ┌──────────┐
                   │  Kafka   │ (变更事件流)
                   └──┬─┬─┬───┘
            ┌────────┘ │ └────────┐
            ▼          ▼          ▼
        ┌──────┐  ┌────────┐  ┌────────┐
        │  ES  │  │ 数仓   │  │ 微服务 │
        └──────┘  └────────┘  └────────┘

数据库变成了整个系统唯一的真实数据源(single source of truth),所有下游都从它派生,且派生过程是自动、实时、一致的。

二、原理:读事务日志,而不是去查表

CDC 最关键的一点是它怎么拿到变更。有两种截然不同的路子,质量差一个数量级。

差的:基于查询(query-based)。定时跑 SELECT * FROM orders WHERE updated_at > ?,对比前后差异。这是"伪 CDC",本质是轮询。

好的:基于日志(log-based)。数据库为了自身崩溃恢复,本来就在持续写一份事务日志——Postgres 叫 WAL(Write-Ahead Log),MySQL 叫 binlog。这份日志里完整记录了每一行数据的变更。CDC 组件假装自己是个"备库",从这份日志里解析出结构化的变更事件。

text
Postgres WAL 片段(简化):
  BEGIN tx=12345
  UPDATE public.orders SET status='paid' WHERE id=99
  COMMIT tx=12345

CDC 解析后发出的事件(简化):
{
  "op": "u",                  // update
  "before": {"id":99,"status":"pending"},
  "after":  {"id":99,"status":"paid"},
  "source": {"tx":12345, ...},
  "ts_ms": 1718000000000
}

为什么 log-based 完胜?三个原因:

  1. 不漏:日志记录了每一次提交,包括同事务里的多次变更,你能拿到完整历史。
  2. 不扰:不需要对业务表加锁、不需要建触发器、不需要扫表,对线上库几乎零侵入。
  3. 顺序保证:日志是按提交顺序写的,下游按顺序消费就是数据的真实演进顺序。

三、工具:Debezium + Kafka 是事实标准,PG 也有原生玩法

Debezium 是目前最主流的开源 CDC 项目,它基于 Kafka Connect 框架运行,为 PG / MySQL / MongoDB / SQL Server 等都提供了 Source Connector。架构是固定的三段式:

text
[ PG / MySQL ] --Debezium Source--> [ Kafka topic ] --消费--> [ 下游 / Sink ]
                                        (名为 pg.public.orders 的 topic)

PG 那一侧需要先开逻辑复制:把 wal_level 设成 logical,建一个 replication slot,Debezium 通过这个 slot 订阅 WAL 的变更流,转成标准的 Kafka 消息发出去。下游可以再接一个 JDBC Sink Connector 写回 ES/ClickHouse,也可以让微服务自己写消费者。

如果嫌 Kafka Connect 那一套太重,PG 生态里还有更轻的选择:原生 logical replication(CREATE PUBLICATION / SUBSCRIPTION),适合 PG→PG 的同步;或者 pglogical 插件。也可以用 Debezium Server(轻量版)直接发到 Pulsar / Kinesis / Redis Stream,跳过 Kafka。

非 Debezium 路线还有 Canal(阿里,MySQL binlog)、Maxwell(MySQL→Kafka,极简)。但如果你要支持多数据库类型、要有完善的 schema 演进处理,Debezium 仍是首选。

四、比双写强在哪:一致性这道坎,双写永远过不去

双写的代码通常长这样:

python
db.execute("INSERT INTO orders ...")
es.index("orders", body=...)        # 万一这里挂了?
redis.set(cache_key, ...)

这里有三个绕不开的坑:

  • 部分失败:DB 写成功、ES 写失败,数据就不一致了。加重试?加事务?跨系统根本没法做事务。加补偿?代码复杂度爆炸。
  • 顺序错乱:并发下,A 的 DB 写成功了但 ES 还没刷新,B 先把 ES 写了——下游看到的是乱序的最终态,不是真实演进。
  • 代码散落:每个写库的地方都要记得同步写下游,新人加一个接口忘写了,就静默不一致。

CDC 把这个责任从"每个写库的业务代码"挪到了"数据库日志"这一个点。应用只管写库,变更会自动且准确地流到下游——因为下游消费的就是数据库真实提交的内容,顺序就是提交顺序,不存在"一个成功一个失败"。一致性是结构性保证,不是靠程序员记性。

五、比轮询强在哪:延迟和负载,都是还不清的债

轮询(sync 脚本每分钟跑一次)看起来简单,实际两个指标都很差:

  • 延迟:分钟级起步。用户改了资料,搜索结果要等下一轮才更新,体验割裂。
  • 负载:每次 SELECT * 全表扫一遍 updated_at > now() - interval,表一大就是灾难;没有合适的索引更是直接打爆 DB。

CDC 是事件驱动:有变更才推,没变更零开销;延迟通常是亚秒级(从提交到下游可见,几百毫秒到几秒)。对线上库的压力来自读 WAL,而不是扫表,量级完全不同。

一句话:轮询是用 DB 的算力换简单,CDC 是用一套中间件换实时和低负载。

六、适用场景:CDC 真正发光的地方

实战里 CDC 最常见、最值得的四个场景:

  1. 同步搜索索引(Elasticsearch / OpenSearch)。DB 是结构化真实源,ES 专门干全文检索——用 CDC 把变更实时灌进 ES,搜索体验丝滑,且天然解耦。
  2. 维护缓存一致性。Redis 缓存 + DB 是经典难题(双删、延迟双删都不优雅)。用 CDC 监听变更、精准失效对应 key,比任何应用层套路都干净。
  3. 数据仓库 / ETL 入仓。把 OLTP 库的变更实时推到 ClickHouse / Snowflake / 数仓,做实时报表,告别 T+1 的离线批处理。
  4. 微服务事件驱动。订单服务改了状态,履约、风控、通知服务都要感知——与其在订单服务里手动 publish,不如让 CDC 把"订单表变更"直接变成事件流,这才是真正的"集成事件"而非"领域事件被滥用"。

一个判断标准:只要"数据库状态变化"本身就是一个有意义的业务事件,就该用 CDC。

七、代价:别假装它免费

CDC 不是银弹,代价实打实:

  • 运维复杂度陡增。你要维护 Debezium + Kafka Connect + Kafka 一整套,监控 lag、处理 rebalance、给 Kafka 留够磁盘。对小团队是实打实的负担。
  • Schema 变更(DDL)是头号雷区。给表加列、改类型、重命名——CDC 组件可能不认识新 schema,导致流中断或事件解析错。Debezium 有 schema registry 配合,但必须配套 CREATE TYPE 之类的谨慎操作,上线前要演练。
  • 顺序与分区矛盾。同一张表要保序就得单分区,单分区又卡吞吐;按主键分区能并发但要自己保证同一主键的事件进同一分区。这是设计期就要想清楚的。
  • 全量 + 增量切换(snapshot)。首次启动要把存量数据先搬一遍,再切到增量。大表 snapshot 期间压力不小,要挑窗口。

小结

  • CDC 的本质:从数据库事务日志(WAL/binlog)捕获变更,事件驱动地推给下游,而非应用双写或定时轮询。
  • 比双写强:一致性是结构性保证,不靠程序员记性;应用代码也回到了"只管写库"的纯粹状态。
  • 比轮询强:亚秒级延迟 + 低侵入负载,而不是分钟级延迟 + 全表扫描。
  • 主流工具栈:Debezium + Kafka Connect + Kafka,PG 生态也有原生 logical replication / pglogical 这类轻量方案。
  • 代价是实打实的:一套中间件的运维、schema 变更的雷、顺序与吞吐的权衡。

一句话:当"数据库的变更"本身就是业务事件,且下游不止一个时,CDC 是最干净的解;否则双写或轮询就够,别为了 CDC 而 CDC。

Related

相关推荐