2. CDC基础概念

在核心银行系统中,账户余额、交易流水、客户信息等数据时刻在变化。下游的信贷风控、反欺诈、数据仓库、实时报表等系统,都需要在业务发生时尽快感知这些变化。传统做法是定时批量抽取(如每晚跑批),延迟高、对源库压力大,还容易漏掉中间状态。CDC(Change Data Capture,变更数据捕获)正是为解决这一问题而生的技术:它识别并捕获数据源中发生的插入、更新、删除操作,将这些变更以事件形式持续输出给下游消费方。

一、CDC的定义与核心价值

CDC的本质是“把数据库内部已经发生的变更,转化为可被外部系统消费的事件流”。它并不重新发明数据,而是复用数据库自身为保证持久性和复制而维护的机制。相比批量抽取,CDC具备三个关键特征:

  • 低延迟:变更提交后毫秒到秒级即可被捕获,支撑准实时场景。
  • 低侵入:基于日志解析时,无需在业务表上增加触发器或时间戳字段,对源库性能影响小。
  • 完整语义:能够区分INSERT、UPDATE、DELETE,并保留变更前后的字段值,便于下游做幂等与回滚。
在核心银行场景中,CDC常被用于:账户余额变动推送、交易流水同步、客户信息变更通知、以及向数据湖/数仓的实时入湖。它把“数据库”从被动查询的数据源,变成了主动发布事件的“事实来源”。

二、日志解析原理:CDC如何“看见”变更

主流CDC工具的核心原理是解析数据库的预写日志(WAL)或二进制日志(binlog)。以MySQL为例,所有对InnoDB表的修改都会先写入redo log和binlog,binlog以事件形式记录变更。CDC工具伪装成MySQL的从库(replica),向主库发送dump协议请求,主库将binlog事件推送给它。工具解析这些事件,还原出表名、操作类型、变更前后的行数据。

# MySQL binlog 事件示例(简化)
# at 1234
# 事件类型: Table_map, Write_rows, Update_rows, Delete_rows
### INSERT INTO bank.account
### SET
###   @1=1001        /* INT meta=0 nullable=0 is_null=0 */
###   @2=5000.00     /* DECIMAL(18,2) */
###   @3='2024-06-01 10:00:00'

对于PostgreSQL,CDC工具通常基于逻辑复制槽(logical replication slot)读取WAL,将变更解码为逻辑日志;Oracle则通过LogMiner或XStream读取redo log。不同数据库的日志格式差异很大,因此CDC工具往往需要针对每种数据库实现专门的解析器。

CDC日志解析与事务一致性示意 核心银行源库 InnoDB / WAL 事务T1: 扣款+记账 COMMIT Binlog / WAL BEGIN UPDATE account... COMMIT (XID=88) CDC解析器 按事务边界聚合 还原before/after 生成变更事件 下游消费者 风控 / 数仓 实时报表 事件总线 事务一致性关键点 • 同一事务的多个变更必须原子输出,不能只发一半 • 按COMMIT顺序投递,避免下游看到中间状态 • 断点续传依赖位点(binlog file+pos / LSN) • 至少一次投递 + 下游幂等 = 端到端不丢不重

三、事务一致性:CDC最容易被忽视的难点

数据库中的一次业务操作往往涉及多行、多表的修改,它们被包裹在一个事务中。如果CDC工具逐行解析并立即发送,下游就可能看到“扣了款但还没记账”的中间状态,导致风控误判或报表对不平。因此,成熟的CDC实现必须保证事务一致性:

  1. 事务边界识别:解析BEGIN和COMMIT事件,将同一事务内的所有变更聚合为一个批次。
  2. 按提交顺序投递:只有事务提交后,才将其变更事件按顺序发送,避免未提交数据外泄。
  3. 位点管理:记录已消费的binlog文件名和偏移量(或LSN),故障重启后从位点继续,保证不丢。
  4. 幂等消费:由于网络重试可能导致重复投递,下游需基于主键或事务ID做去重,实现“至少一次+幂等=精确一次”的效果。
常见误区:认为CDC工具会自动保证端到端精确一次。实际上,大多数工具只保证“至少一次”,精确一次需要下游配合幂等写入。在核心银行场景中,余额变动事件若重复消费,可能导致重复记账,必须设计去重键。

四、常见CDC工具对比与选型

不同工具在侵入性、延迟、事务支持、运维成本上差异明显。下表从核心银行实战角度进行对比:

工具 原理 事务一致性 延迟 适用场景
Debezium 解析binlog/WAL,基于Kafka Connect 强,按事务聚合 毫秒~秒级 MySQL/PG/Oracle实时入湖、事件驱动
Canal 伪装MySQL从库解析binlog 较好,支持事务 毫秒级 阿里系MySQL生态,轻量级同步
Flink CDC Flink算子内嵌Debezium 强,支持Exactly-Once 毫秒~秒级 流批一体、复杂ETL、实时数仓
Oracle GoldenGate 解析redo log 强,成熟稳定 亚秒级 Oracle核心系统,商业支持
触发器方案 在表上建触发器写变更表 依赖触发器实现 秒级 无日志权限的遗留系统,但侵入大
选型建议:核心银行若以MySQL为主,Debezium或Flink CDC是主流选择;若已有Kafka生态,Debezium天然契合;若需要复杂流式计算,Flink CDC更合适;Oracle核心系统则优先评估GoldenGate。触发器方案仅作为无日志权限时的兜底,需谨慎评估对源库的性能影响。

五、案例复盘:一次余额事件重复消费的排查

某银行将账户余额变更通过Debezium推送到Kafka,下游风控系统消费后更新缓存。上线后发现偶发缓存余额比实际多出一笔。排查发现:Debezium在Kafka Connect重启后,因位点提交策略为“至少一次”,部分事件被重复投递;而风控消费端未做幂等,导致同一笔扣款被应用两次。修复方案是在消费端以“账户号+交易流水号”作为去重键,写入Redis并设置TTL,重复事件直接丢弃。同时将Debezium的offset.flush.interval.ms调小,减少重复窗口。该案例说明:CDC的事务一致性不仅取决于工具,更取决于端到端的消费设计。

理解CDC的定义、日志解析原理、事务一致性机制和工具差异,是构建核心银行实时事件分发体系的基础。只有把“变更如何产生、如何捕获、如何保证不丢不重”这条链路想清楚,下游的事件分发才能稳定可靠。