Flink 2.3.0 从理论到实践 ------ 第 14 章 Flink CDC 3.x 数据同步
课程定位:第五篇数据集成续。Kafka 入报文后,MySQL 维表如何同步入湖?本章讲解 CDC 原理(Binlog/Debezium)、Flink CDC 3.x Pipeline YAML 架构、MySQL CDC 全量+增量与 Schema 演进、Kafka/Paimon CDC、Transform 模块(投影/过滤/计算列/路由)、多 Sink 路由(Doris + Paimon)、断点续传与 Exactly-Once------这是维表入湖与整库同步的核心通道。
版本基线:Flink 2.3.0 + Flink CDC 3.6.x(适配 Flink 2.3)+ MySQL 8.0/8.4 + Paimon 1.4.x + Doris 4.1
配套教程:Apache Flink CDC 3.5.0 从理论到实践(基础理论)
章节导读
- [14.1 CDC 原理:Binlog 与 Debezium](#14.1 CDC 原理:Binlog 与 Debezium)
- [14.2 Flink CDC 3.x 架构(Pipeline / YAML)](#14.2 Flink CDC 3.x 架构(Pipeline / YAML))
- [14.3 MySQL CDC:全量 + 增量、Schema 演进](#14.3 MySQL CDC:全量 + 增量、Schema 演进)
- [14.4 Kafka CDC / Paimon CDC](#14.4 Kafka CDC / Paimon CDC)
- [14.5 Transform 模块:投影、过滤、计算列、路由](#14.5 Transform 模块:投影、过滤、计算列、路由)
- [14.6 多 Sink 路由:Doris + Paimon](#14.6 多 Sink 路由:Doris + Paimon)
- [14.7 断点续传与 Exactly-Once](#14.7 断点续传与 Exactly-Once)
- [14.8 本章小结与下章预告](#14.8 本章小结与下章预告)
14.1 CDC 原理:Binlog 与 Debezium
14.1.1 什么是 CDC
CDC(Change Data Capture,变更数据捕获)是捕获数据库增量变更的技术:源库每条 INSERT/UPDATE/DELETE 实时捕获,下游同步消费。
┌──────────────────────────────────────────────────────────────┐
│ CDC 同步 vs 批量同步 │
└──────────────────────────────────────────────────────────────┘
批量同步(定时全量): CDC 同步(实时增量):
┌────────┐ 每天 00:00 ┌────────┐ 实时
│ MySQL │ ──全量拉取──► │ MySQL │ ──binlog──►
└────────┘ └────────┘
│ │
▼ ▼
滞后 1 天,带宽大 延迟秒级,增量小
| 维度 | 批量同步 | CDC 同步 |
|---|---|---|
| 延迟 | 小时~天 | 秒级 |
| 带宽 | 全表扫描 | 增量传输 |
| 源库压力 | 全表锁 | binlog 读 |
| 实时性 | ❌ | ✅ |
14.1.2 MySQL Binlog
MySQL Binlog(二进制日志)记录所有已提交的 DDL/DML,是 CDC 的数据源。
| Binlog 格式 | 说明 | CDC 支持 |
|---|---|---|
STATEMENT |
SQL 语句 | ❌(不确定性) |
ROW |
行级变更(before/after) | ★ CDC 必需 |
MIXED |
混合 | ⚠️(部分可读) |
sql
-- MySQL 开启 ROW 格式 binlog(CDC 前置)
SET GLOBAL binlog_format = 'ROW';
SET GLOBAL binlog_row_image = 'FULL'; -- 记录完整 before/after
项目硬约束 :MySQL CDC 要求
binlog_format=ROW+binlog_row_image=FULL。binlog_row_image=MINIMAL只记变更列,before 不全,CDC 还原 UPDATE/DELETE 会丢字段。
14.1.3 Debezium
Debezium 是基于 Kafka Connect 的 CDC 框架,将 binlog 转为 Kafka 消息(debezium-json 格式)。Flink CDC 内部借鉴 Debezium 的 binlog 解析 ,但不依赖 Kafka Connect------直连 MySQL,无需中间 Kafka。
| 维度 | Debezium(Kafka Connect) | Flink CDC(直连) |
|---|---|---|
| 架构 | MySQL → Kafka → Flink | MySQL → Flink |
| 中间件 | 需 Kafka + Connect | ❌ 无需 |
| 全量+增量 | 需额外工具 | ✅ 内置全量+增量 |
| 部署 | 复杂 | 简单 |
| 一致性 | 需保障 | ★ Flink EOS |
选型 :已有 Kafka Connect 生态 → Debezium +
debezium-json格式(第 13 章);无 Kafka 中间件 → Flink CDC 直连。车联网项目用 Flink CDC 直连,维表直接入 Paimon。
14.2 Flink CDC 3.x 架构(Pipeline / YAML)
14.2.1 3.x 架构演进
┌──────────────────────────────────────────────────────────────┐
│ Flink CDC 架构演进 │
└──────────────────────────────────────────────────────────────┘
2.x(SourceFunction): 3.x(Pipeline YAML):
┌──────────┐ ┌──────────┐
│ MySQL │ ──Source──► │ source │ (MySQL/Kafka/Paimon)
│ Source │ Flink SQL │ │
└──────────┘ 单表 ├──────────┤
无路由 │ transform│ (投影/过滤/计算列/路由)
│ │
├──────────┤
│ sink │ (Doris/Paimon/Kafka)
│ 多 sink │
└──────────┘
整库同步 + 多 sink 路由
| 版本 | 特性 |
|---|---|
| 2.x | 单表 Source(SourceFunction),Flink SQL 消费 |
| 3.x | Pipeline YAML:整库同步、Transform 模块、多 Sink 路由、Schema 演进 |
14.2.2 Pipeline YAML 四段式
yaml
source: # ① 数据源
type: mysql
...
pipeline: # ② 管道配置
name: ...
parallelism: 2
schema.change.behavior: evolve
transform: # ③ 变换(可选)
- source-table: ...
projection: ...
filter: ...
sink: # ④ 目的地
type: paimon / doris / kafka
...
route: # ⑤ 路由(可选,source → sink 映射)
- source-table: ...
sink-table: ...
| 段 | 作用 | 必填 |
|---|---|---|
source |
数据源类型与连接 | ✅ |
pipeline |
管道名、并行度、Schema 演进 | ✅ |
transform |
投影/过滤/计算列 | 可选 |
sink |
目的地类型与连接 | ✅ |
route |
source 表 → sink 表映射 | 可选 |
14.3 MySQL CDC:全量 + 增量、Schema 演进
14.3.1 全量 + 增量无缝切换
Flink CDC MySQL Source 支持全量+增量一体化:先全量快照,再切 binlog 增量,无锁。
┌──────────────────────────────────────────────────────────────┐
│ MySQL CDC 全量+增量切换 │
└──────────────────────────────────────────────────────────────┘
① 全量阶段(快照) ② 切换点 ③ 增量阶段(binlog)
SELECT * FROM table 记录 binlog 位点 消费 binlog
分片并发读 ──────────────► 从位点续读
无锁(基于 MVCC) (一致性切点) 不重不丢
| 阶段 | 机制 | 锁 |
|---|---|---|
| 全量快照 | 分片 SELECT,基于 MVCC 快照 | 无锁 |
| 切换点 | 记录 binlog 位点 + 全量完成标记 | 短暂(位点记录) |
| 增量消费 | 从位点读 binlog | 无锁 |
2.x 的痛点 :旧 CDC 全量阶段用
FLUSH TABLES WITH READ LOCK(全局读锁),影响在线业务。3.x 基于 MVCC 快照无锁全量,生产可用。
14.3.2 项目实战:MySQL 维表整库同步入 Paimon
车联网项目 MySQL 维表(车型/车系/车辆信息)用 Flink CDC YAML pipeline 同步入湖:
yaml
source:
type: mysql
hostname: xxx.xxx.xxx.xxx
port: 3306
username: xxx
password: xxx
tables: iov_veh.t_cn_car_info,iov_veh.t_cn_car_series, iov_veh.t_cn_car_type
server-id: 5410-5418 # ★ server-id 区间,多并行度防冲突
server-time-zone: Asia/Shanghai
sink:
type: paimon
catalog.properties.metastore: hive
catalog.properties.uri: thrift://hm-1:9083,thrift://hm-2:9083
catalog.properties.warehouse: hdfs:///warehouse/tablespace/managed/hive
pipeline:
name: vehicle_dim_to_tsp_ods_paimon
parallelism: 2
schema.change.behavior: evolve # ★ MySQL DDL 变更自动演进到 Paimon
route:
- source-table: iov_veh.t_cn_car_info
sink-table: tsp_ods.ods_iov_veh_t_cn_car_info
- source-table: iov_veh.t_cn_car_series
sink-table: tsp_ods.ods_iov_veh_t_cn_car_series
- source-table: iov_veh.t_cn_car_type
sink-table: tsp_ods.ods_iov_veh_t_cn_car_type
| 要点 | 说明 |
|---|---|
schema.change.behavior: evolve |
★ 源端加列自动同步,维表结构变更零人工 |
server-id 配区间 |
★ 增量读取按并行度拆分 binlog 位点,配单个 id 多并行会冲突 |
| 维表入湖后用途 | 实时作业 FOR SYSTEM_TIME AS OF 做 LOOKUP JOIN;离线批作业直接批读------一份维表两链路共享 |
项目硬约束① :
server-id必须配区间(如5410-5418),不要配单个------多并行度时各 SubTask 抢同一个 server-id 会冲突,binlog 读取失败。项目硬约束② :维表入湖后,实时作业用
FOR SYSTEM_TIME AS OF做 LOOKUP JOIN (见第 11 章),离线批作业直接批读------一份维表两链路共享,不需要单独维护实时维表。
14.3.3 Schema 演进
schema.change.behavior 控制 MySQL DDL 变更如何同步到下游:
| 取值 | 行为 | 适用 |
|---|---|---|
evolve |
★ 自动加列、改类型 | 维表(推荐) |
exception |
DDL 变更报错停止 | 严格管控 |
lenient |
宽松(忽略不兼容) | 容错 |
ignore |
忽略 DDL | 仅同步 DML |
项目实践 :维表用
evolve------MySQL 加列自动同步到 Paimon,维表结构变更零人工。报文事实表 ODS 不用 CDC(Kafka 入湖),不涉及 Schema 演进。
14.3.4 提交方式
bash
# 提交 YAML pipeline
flink-cdc.sh mysql-to-paimon.yaml \
--flink-home /opt/flink \
--jar flink-cdc-dist.jar
# 或用 sql-job 提交
flink run -c org.apache.flink.cdc.cli.CliFrontend \
flink-cdc-dist.jar \
mysql-to-paimon.yaml
项目硬约束(jar 加载) :CDC jar 与连接器 jar 同样遵循"jar 加载铁律"------只放一份到
${FLINK_HOME}/lib,不要-j。-j双 classloader 加载报 SPInot a subtype。判据:CDC jar 1 个 + Paimon jar 1 个 + 连接器版本唯一。
14.4 Kafka CDC / Paimon CDC
14.4.1 Kafka CDC Source
Flink CDC 3.x 支持以 Kafka 为 CDC Source,消费 Canal/Debezium/Maxwell 格式消息:
yaml
source:
type: kafka
topic: cnx_vehicle_dim_cdc
properties.bootstrap.servers: kafka-1:9092
properties.group.id: ${group.id}
scan.startup.mode: group-offsets
value.format: debezium-json # ★ CDC 格式
key.format: json
| Source | 用途 | 中间件 |
|---|---|---|
| MySQL CDC | 直连 MySQL | 无 |
| Kafka CDC | 消费已有 CDC 消息 | 需 Kafka + Canal/Debezium |
选型:已有 Canal/Debezium 写 Kafka 的存量系统 → Kafka CDC Source;新建链路 → MySQL CDC 直连,更简单。
14.4.2 Paimon CDC Source
Paimon 表本身可作为 CDC Source(流读 changelog):
yaml
source:
type: paimon
catalog.properties.metastore: hive
catalog.properties.uri: thrift://hm-1:9083
catalog.properties.warehouse: hdfs:///...
tables: tsp_ods.ods_iov_veh_t_cn_car_info
scan.mode: latest # 流读模式
sink:
type: doris
...
| 场景 | 用法 |
|---|---|
| Paimon 维表 → Doris | Paimon CDC Source + Doris Sink |
| Paimon 中间结果 → 下游表 | Paimon CDC Source + Paimon Sink |
项目硬约束 :Paimon CDC Source 流读必配
scan.mode=latest(非默认latest-full),否则全量回放 OOM(见第 11 章)。
14.5 Transform 模块:投影、过滤、计算列、路由
14.5.1 Transform 四能力
yaml
transform:
- source-table: iov_veh.t_cn_car_info
sink-table: tsp_ods.ods_car_info_clean
projection: vin, car_series, car_type, UPDATE_TIME AS update_time
filter: STATUS = 1 AND vin IS NOT NULL
- source-table: iov_veh.t_cn_car_type
sink-table: tsp_ods.ods_car_type_full
projection: id, type_name, CREATE_TIME AS create_time
| 能力 | 语法 | 作用 |
|---|---|---|
| 投影 | projection: col1, col2 |
选列 |
| 计算列 | col_expr AS alias |
派生列 |
| 过滤 | filter: condition |
行筛选 |
| 路由 | route 段 |
表映射 |
14.5.2 实战:维表清洗
yaml
transform:
- source-table: iov_veh.t_cn_car_info
sink-table: tsp_ods.ods_car_info_clean
# 投影:选列 + 计算列(脱敏 + 重命名)
projection: >
VinMask(vin) AS masked_vin,
car_series,
car_type,
CASE WHEN status=1 THEN 'ACTIVE' ELSE 'INACTIVE' END AS car_status,
UPDATE_TIME AS update_time
# 过滤:只同步有效车辆
filter: vin IS NOT NULL AND LENGTH(vin) = 17
UDF 在 Transform 中 :Transform 的
projection/filter可用已注册的 UDF(如VinMask)。UDF jar 放${FLINK_HOME}/lib。
14.5.3 路由:多表分流
yaml
route:
# 车辆信息 → 维表
- source-table: iov_veh.t_cn_car_info
sink-table: tsp_ods.ods_car_info
# 车辆类型 → 维表 + Doris 镜像(一路两写)
- source-table: iov_veh.t_cn_car_type
sink-table: tsp_ods.ods_car_type, doris_dim.dim_car_type
一路两写:route 可把一张源表路由到多个 sink 表,实现"Paimon 入湖 + Doris 镜像"同步------不需要两条作业。
14.6 多 Sink 路由:Doris + Paimon
14.6.1 多 Sink 配置
yaml
sink:
- type: paimon
name: paimon-sink
catalog.properties.metastore: hive
catalog.properties.uri: thrift://hm-1:9083
catalog.properties.warehouse: hdfs:///...
- type: doris
name: doris-sink
fenodes: doris-fe-1:28030
username: ${doris.user}
password: ${doris.pwd}
sink.enable-2pc: true
sink.label-prefix: cdc-dim-doris
route:
- source-table: iov_veh.t_cn_car_info
sink-table: paimon-sink.tsp_ods.ods_car_info
- source-table: iov_veh.t_cn_car_type
sink-table: doris-sink.doris_dim.dim_car_type
| Sink | 特性 | 适用 |
|---|---|---|
| Paimon | 湖存储、批流共享 | ★ 维表入湖 |
| Doris | 查询加速、Unique Key upsert | BI 点查 |
| Kafka | append 流 | 中转/审计 |
项目实践:维表入 Paimon(两链路共享),明细表镜像到 Doris(BI 点查)。一份 CDC 作业,两份落地,零额外开发。
14.6.2 Doris Sink 的 2PC
Doris Sink 配 sink.enable-2pc=true 参与 Flink Checkpoint 两阶段提交------Checkpoint 成功才 commit,失败 abort,保证 Exactly-Once。sink.label-prefix 每作业唯一(见第 11 章)。
14.7 断点续传与 Exactly-Once
14.7.1 断点续传原理
┌──────────────────────────────────────────────────────────────┐
│ CDC 断点续传 │
└──────────────────────────────────────────────────────────────┘
全量快照 ──► binlog 位点 P1 ──► Checkpoint(记 P1) ──► 增量消费
│
作业故障 │
▼
从 P1 恢复 ──► 续读 binlog(P1 之后)
(不重全量,不丢不重)
| 机制 | 说明 |
|---|---|
| 全量阶段位点 | 快照分片 + binlog 位点记入 Checkpoint |
| 增量阶段位点 | binlog 文件名 + position 记入 Checkpoint |
| 故障恢复 | 从最近 Checkpoint 的位点续读,不重全量 |
14.7.2 Exactly-Once 保障
| 环节 | EOS 保障 |
|---|---|
| Source(MySQL CDC) | binlog 位点记入 Checkpoint |
| Transform | 无状态(投影/过滤) |
| Sink(Paimon) | checkpoint commit(主键 upsert 幂等) |
| Sink(Doris) | 2PC(enable-2pc=true) |
端到端 EOS:Source 位点 + Sink 2PC/幂等,故障时从 Checkpoint 恢复,源重放 + 幂等去重,不重不丢。
14.7.3 改表无状态重启
项目硬约束 :表重建后作业
OutOfRangeException崩溃循环------根因是有状态恢复,源表快照不连续。解法:停作业 → DDL → 无状态重启(不恢复 checkpoint,从最新位点起)。CDC 作业改表同样遵循此铁律。
| 操作 | 是否可状态恢复 | 风险 |
|---|---|---|
加列(evolve) |
✅ | 无 |
| 改类型 | ⚠️ | 可能不兼容 |
| 重建表(DROP+CREATE) | ❌ | 快照断裂,必须无状态重启 |
| 改 bucket 数 | ❌ | 同上 |
14.7.4 Savepoint 管理
bash
# 停作业前做 savepoint(CDC 作业改表/升级必用)
flink stop --savepointPath hdfs:///savepoints/cdc-dim <jobId>
# 从 savepoint 重启(状态恢复)
flink run -s hdfs:///savepoints/cdc-dim/savepoint-xxx \
-c org.apache.flink.cdc.cli.CliFrontend \
flink-cdc-dist.jar mysql-to-paimon.yaml
Savepoint vs Checkpoint:Savepoint 是手动触发的全量状态快照,用于版本升级/计划停机;Checkpoint 是自动增量的容错快照。CDC 作业改表前先做 Savepoint,重启可恢复。
14.8 本章小结与下章预告
本章小结
┌────────────────────────────────────────────────────────────────┐
│ 第 14 章 要点回顾 │
└────────────────────────────────────────────────────────────────┘
✓ CDC 原理:
Binlog(ROW 格式 + FULL image)是数据源
Debezium 基于 Kafka Connect;Flink CDC 直连(无中间件)
✓ Flink CDC 3.x:
Pipeline YAML 四段: source / pipeline / transform / sink
整库同步 + 多 sink 路由 + Schema 演进
✓ MySQL CDC:
全量+增量无锁切换(MVCC 快照)
★ server-id 配区间(多并行防冲突)
★ schema.change.behavior=evolve(加列自动同步)
★ jar 加载铁律: 只放 lib,绝不传 -j
✓ Kafka CDC / Paimon CDC:
Kafka CDC: 消费 Canal/Debezium 格式
Paimon CDC: 流读 changelog(scan.mode=latest)
✓ Transform:
投影 / 计算列 / 过滤 / 路由
一路两写: Paimon 入湖 + Doris 镜像
✓ 多 Sink 路由:
Paimon(湖存储,两链路共享)
Doris(BI 加速,2PC)
一份 CDC,两份落地
✓ 断点续传 + EOS:
binlog 位点记 Checkpoint
Sink 2PC/幂等
★ 重建表必须无状态重启(快照断裂)
Savepoint 用于版本升级/计划停机
下章预告
第 15 章 Paimon & Doris 湖仓写入:CDC 维表入湖后,如何写事实表与服务层。讲解 Paimon 写入(主键表/Append 表/Changelog Producer)、Paimon 读取(批读/流读/Lookup)、Paimon Catalog 注册、Doris Sink(Stream Load/2PC/部分列更新)、Doris Read(JDBC vs Doris Connector)、端到端 Exactly-Once 链路、湖仓一体 Flink+Paimon+Doris 协同。
官方参考资料
- Flink CDC 文档:https://nightlies.apache.org/flink/flink-cdc-docs-stable/
- MySQL CDC Connector:https://nightlies.apache.org/flink/flink-cdc-docs-stable/docs/connectors/pipeline-connectors/mysql/
- Paimon CDC Connector:https://nightlies.apache.org/flink/flink-cdc-docs-stable/docs/connectors/pipeline-connectors/paimon/
- Doris CDC Connector:https://nightlies.apache.org/flink/flink-cdc-docs-stable/docs/connectors/pipeline-connectors/doris/
- Transform:https://nightlies.apache.org/flink/flink-cdc-docs-stable/docs/transformer/
- Pipeline YAML:https://nightlies.apache.org/flink/flink-cdc-docs-stable/docs/developer-guide/understand-flink-cdc-api/