4. Flink CDC核心概念
在金融主数据同步场景中,数据一致性不是“尽量做到”,而是“必须做到”。账户、客户、产品、币种、机构等主数据一旦出现重复、丢失或乱序,下游核算、风控与报表都会连锁失真。理解 Flink CDC 的核心概念,本质上是在理解“变更如何被捕获、如何被处理、如何被可靠写入”这条链路。本节围绕四个关键点展开:Flink CDC 架构、Source 与 Sink、Exactly-Once 语义、增量快照算法。
一、Flink CDC 架构:从数据库日志到下游存储
Flink CDC 的架构可以理解为三层协作:捕获层、计算层、写入层。捕获层通过 Debezium 等连接器读取 MySQL、Oracle、PostgreSQL 的 binlog 或 redo log,将行级变更解析为 INSERT、UPDATE、DELETE 事件;计算层由 Flink 作业承担,负责反序列化、过滤、聚合、维表关联与状态管理;写入层则通过 Sink 连接器将结果写入 Kafka、Hudi、Iceberg、Doris、StarRocks 或另一个数据库。
与传统的“定时批量抽取”相比,Flink CDC 的关键差异在于:它先做一次全量快照,再无缝切换到增量日志读取,整个过程不需要锁表,也不需要人工拼接全量与增量。对于金融主数据,这意味着白天业务持续写入时,同步链路依然可以稳定运行。
二、Source 与 Sink:数据进出 Flink 的两端
Source 负责“读进来”,Sink 负责“写出去”。在 Flink CDC 中,Source 通常使用 MySqlSource、OracleSource 或 PostgresSource,它们内部封装了 Debezium 的变更事件格式。Sink 则根据目标存储选择,例如 Kafka Sink、JDBC Sink、Hudi Sink、Iceberg Sink 或 Doris Sink。
金融主数据同步中,Source 与 Sink 的配置直接决定同步质量。以下是一个典型的 MySQL CDC Source 配置片段:
MySqlSource<String> source = MySqlSource.<String>builder()
.hostname("10.20.30.40")
.port(3306)
.databaseList("finance_mdm")
.tableList("finance_mdm.customer", "finance_mdm.account")
.username("cdc_user")
.password("******")
.deserializer(new JsonDebeziumDeserializationSchema())
.startupOptions(StartupOptions.initial())
.build();
其中 StartupOptions.initial() 表示先做全量快照,再切换到增量日志。若改为 latest(),则只从当前位点开始读取,历史数据不会同步。对于主数据初始化场景,通常必须使用 initial()。
三、Exactly-Once 语义:金融同步的底线
Exactly-Once 意味着每条变更事件在最终结果中“恰好生效一次”,既不重复也不丢失。Flink 通过 Checkpoint 机制 与 两阶段提交 实现端到端的 Exactly-Once。Checkpoint 会周期性保存 Source 的读取位点、算子状态以及 Sink 的预提交信息;当作业失败恢复时,从最近一次成功的 Checkpoint 重新开始。
但要注意:Exactly-Once 不是“自动获得”的。它要求 Source 支持位点回放、Sink 支持事务或幂等写入。例如 Kafka Sink 在开启 EXACTLY_ONCE 语义后,会使用事务将一批消息原子写入;而某些 JDBC Sink 若不支持事务,则只能做到 At-Least-Once,需要下游通过主键去重来补偿。
| 语义 | 含义 | 金融主数据适用性 |
|---|---|---|
| At-Most-Once | 可能丢失,不重复 | 不适用,主数据不能丢 |
| At-Least-Once | 不丢失,可能重复 | 需下游幂等去重 |
| Exactly-Once | 不丢失,不重复 | 推荐,需 Source/Sink 配合 |
四、增量快照算法:全量与增量的无缝衔接
增量快照算法是 Flink CDC 最核心的机制之一。传统方案需要先锁表做全量,再记录位点,再切换增量,期间容易漏数据或重复。Flink CDC 的做法是:将全量快照拆分为多个分片,每个分片独立读取,同时记录读取前后的 binlog 位点;当所有分片完成后,再从最早的分片位点开始消费增量日志,从而保证全量与增量之间不丢不重。
其关键流程可以概括为:
- 记录当前 binlog 位点作为低水位。
- 对表进行分片,并行读取全量数据。
- 每个分片完成后记录高水位位点。
- 所有分片完成后,从低水位开始回放增量日志。
- 结合 Checkpoint 持续保存进度,失败后从最近位点恢复。
五、案例复盘:客户主数据同步中的一次重复写入
某金融项目在同步客户主数据时,发现下游 Doris 表中出现少量重复客户记录。排查后发现:Sink 使用的是 JDBC 写入,未开启事务,而作业在 Checkpoint 前发生了一次 TaskManager 重启。由于 Source 从 Checkpoint 恢复,部分事件被重新发送,JDBC Sink 又直接 INSERT,导致重复。
修复方式有两种:一是将 Sink 改为支持事务的 Kafka 或 Hudi,并开启 Exactly-Once;二是在 JDBC Sink 前增加按主键去重的算子,或使用 INSERT INTO ... ON DUPLICATE KEY UPDATE 实现幂等写入。最终该项目采用“Kafka 缓冲 + Doris 主键模型”的方案,既保证了 Exactly-Once,又提升了写入吞吐。
这个案例说明:Exactly-Once 是链路级能力,不是单个组件的能力。Source 能回放、Flink 能 Checkpoint、Sink 能幂等或事务,三者缺一不可。理解 Flink CDC 的架构、Source/Sink、Exactly-Once 与增量快照算法,才能在设计金融主数据同步方案时,把“一致性”从口号变成可落地的工程配置。