一、背景:为什么需要 CDC?
1.1 传统数据集成的痛点
在数据仓库和数据湖建设中,将业务数据库的数据同步到分析系统是最基础也最关键的环节。传统方案主要有两种:
| 方案 | 原理 | 问题 |
|---|---|---|
| 全量定时抽取(如 DataX/Sqoop) | 定期 SELECT 全表数据 | 延迟高(通常 T+1)、资源浪费大、对源库压力大 |
| 基于时间戳增量抽取 | 按 update_time 字段拉取增量 | 无法捕获删除操作、依赖业务表设计、时间窗口容易遗漏数据 |
这两种方式的核心缺陷在于无法实时感知数据变更,且对业务系统存在侵入性。
1.2 CDC 技术的演进
CDC(Change Data Capture,变更数据捕获)技术通过监听数据库日志(如 MySQL Binlog、PostgreSQL WAL)来捕获数据变更,天然具备:
- 实时性:变更即时捕获,端到端延迟可达亚秒级
- 完整性:INSERT / UPDATE / DELETE 全部捕获,不遗漏
- 低侵入:读取日志而非查询表,对源库几乎无影响
CDC 领域的关键里程碑:
| 时间 | 事件 | 意义 |
|---|---|---|
| 2011 | LinkedIn 开源 Databus | 早期 CDC 方案,绑定特定数据库 |
| 2017 | Debezium 1.0 发布 | 基于 Kafka Connect 的通用 CDC 框架 |
| 2020 | Flink CDC 1.0 发布 | 将 CDC 与流计算深度融合 |
| 2021 | Flink CDC 2.0 发布 | 增量快照算法,无锁并发读取 |
| 2023.12 | Flink CDC 3.0 发布 | YAML Pipeline,端到端数据集成平台 |
| 2024.05 | Flink CDC 3.1(加入 Apache Flink 子项目) | transform + route 能力 |
| 2025.05 | Flink CDC 3.4 发布 | 新增 Iceberg sink、Batch 执行模式 |
1.3 Flink CDC 的定位
Flink CDC 是 Apache Flink 的子项目,定位为「分布式实时数据集成工具」,它可以把数据库表的全量数据和增量变更数据接入 Flink,使数据库表在 Flink 中表现为一张持续变化的动态表。与传统 Debezium + Kafka 方案相比,它最大的差异是去除了中间件依赖,将 Source → Transform → Sink 整合为单一 Flink 作业,降低了架构复杂度和运维成本。
从使用视角看,Flink CDC 通常有两类能力:
- Flink SQL / Table API Source Connector
- 在 Flink SQL 中通过
CREATE TABLE ... WITH ('connector' = 'mysql-cdc')等方式定义 CDC 源表; - 适合实时 ETL、实时宽表、实时数仓建模。
- 在 Flink SQL 中通过
- Flink CDC Pipeline / Data Integration 能力
- 通过 YAML 等声明式方式定义 source、sink、route、transform 等;
- 更偏向端到端数据同步与整库同步。
二、架构设计与核心原理
2.1 整体架构(Flink CDC 3.x)
Flink CDC 3.x 采用四层架构设计:

2.2 数据模型抽象
Flink CDC 将外部系统的存储对象统一抽象为「表(Table)」,每个表由 Table ID(namespace + schemaName + tableName 三元组)唯一标识。
内部数据流基于 Event 接口传输,分为两类:
- DataChangeEvent:数据变更事件,包含
before(变更前)和after(变更后)字段值,类型有 INSERT / UPDATE / DELETE / REPLACE - SchemaChangeEvent:结构变更事件,包含 AddColumn、DropColumn、AlterColumnType、RenameColumn、CreateTable、DropTable、TruncateTable 等
2.3 核心算法:增量快照(Incremental Snapshot)
增量快照算法是 Flink CDC 的核心创新(借鉴 Netflix DBLog 论文),解决了传统 CDC 全量阶段需要锁表的致命问题。

关键优势:
- 无锁设计:全程不需要
FLUSH TABLES WITH READ LOCK,对线上业务零影响 - 并发读取:多个 SourceReader 并行处理不同 Chunk,水平扩展能力强
- 断点续传:基于 Flink Checkpoint 机制,支持 Chunk 粒度的故障恢复
- Exactly-Once 语义:结合 Flink 的两阶段提交,保证端到端精确一致
2.4 Schema Evolution 机制
Flink CDC 3.x 支持上游 DDL 变更的实时同步,通过 SchemaOperator 和 SchemaRegistry 协调实现(schema.change.behavior):
| 行为模式 | 配置值 | 说明 |
|---|---|---|
| 异常中断 | exception | 遇到 DDL 变更直接抛出异常 |
| 严格应用 | evolve | 强制将 DDL 变更同步到下游,失败则触发 Failover |
| 尝试应用 | try_evolve | 尝试同步 DDL,失败则忽略继续 |
| 宽松模式 | lenient(默认) | 仅应用下游兼容的 DDL 变更 |
| 完全忽略 | ignore | 忽略所有 DDL 变更 |
三、优缺点对比分析
3.1 核心优势
| 优势 | 说明 |
|---|---|
| 架构简洁 | 单一 Flink 作业,无需 Kafka/Kafka Connect 等中间件 |
| 无锁全量读取 | 增量快照算法,全量阶段不锁表、不影响在线业务 |
| 并发水平扩展 | 全量阶段多并发读取,增量阶段可按表并行 |
| 端到端 Exactly-Once | 结合 Flink Checkpoint + 两阶段提交保障一致性 |
| Schema 自动演进 | 上游 DDL 变更可自动同步到下游 |
| 声明式 YAML | 零 Java 代码,YAML 定义全链路同步 |
| 整库同步 | 一个作业可同步整个数据库所有表 |
| 低延迟 | 省去 Kafka 中间层,亚秒级端到端延迟 |
3.2 局限与不足
| 局限 | 说明 | 应对建议 |
|---|---|---|
| Sink 单一消费 | 一条 Pipeline 只能写入一个 Sink,不支持广播到多消费者 | 需要多消费者时仍需引入 Kafka |
| 生态宽度有限 | Sink 连接器不如 Kafka 生态丰富 | 3.x 版本在持续扩展(已支持 Doris/StarRocks/Paimon/Iceberg/Fluss 等) |
| 大状态管理压力 | 整库同步时 State 可能很大,对 RocksDB 和 Checkpoint 存储要求高 | 合理设置并行度、使用增量 Checkpoint |
| 运维成熟度 | 相比 Debezium+Kafka 方案,社区运维经验积累相对较少 | 建立完善的监控体系 |
| Schema 变更风险 | 复杂 DDL(如列类型收窄)可能导致下游兼容问题 | 使用 try_evolve 或 lenient 模式 |
四、适用场景
4.1 推荐使用 Flink CDC 的场景
- 实时数据入仓/入湖:MySQL/PostgreSQL → Doris/StarRocks/Paimon/Iceberg,追求低延迟和简单架构
- 整库同步:将业务库全量表一次性同步到分析系统,减少连接占用
- 分库分表合并:通过
route规则将上游分片表路由合并到下游单表 - 实时 ETL 流水线:结合
transform做列投影、过滤、计算列等轻量 ETL - 实时物化视图:数据库变更驱动的实时聚合/指标计算
- 数据库迁移:利用全量+增量无缝切换实现在线迁移
4.2 不推荐使用 Flink CDC 的场景
- 需要多下游消费同一数据源:此时 Kafka 作为解耦层更合适
- 需要数据回放能力:Kafka 天然支持按 offset 回放
- 源端非关系型数据库且无成熟连接器:需确认连接器可用性
- 超大规模(数千张表)且表间有复杂依赖:单一 Pipeline 的状态管理可能成为瓶颈
五、配置详解(Flink CDC 3.x YAML Pipeline)
5.1 配置结构总览
一个完整的 Flink CDC YAML Pipeline 由以下部分组成:
yaml
source: # [必填] 数据源配置
type: mysql
...
sink: # [必填] 数据目标配置
type: doris
...
route: # [可选] 表路由规则
- source-table: ...
sink-table: ...
transform: # [可选] 数据转换规则
- source-table: ...
projection: ...
filter: ...
pipeline: # [必填] 流水线全局配置
name: ...
parallelism: ...
5.2 Source 配置(以 MySQL 为例)
核心连接参数
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
| type | 是 | - | 连接器类型,如 mysql |
| hostname | 是 | - | 数据库主机地址 |
| port | 否 | 3306 | 数据库端口 |
| username | 是 | - | 用户名 |
| password | 是 | - | 密码 |
| tables | 是 | - | 监控的表,支持正则(如 db..*) |
| tables.exclude | 否 | - | 排除的表 |
| server-id | 否 | - | MySQL server-id,集群中需唯一(如 5400-5404) |
快照与增量参数
| 参数 | 默认值 | 说明 |
|---|---|---|
| scan.startup.mode | initial | 启动模式:initial(全量+增量)、latest-offset(仅增量)、snapshot(仅全量)等 |
| scan.incremental.snapshot.chunk.size | 8096 | Chunk 行数,影响全量阶段并发粒度 |
| scan.snapshot.fetch.size | 1024 | 每次 fetch 的最大行数 |
| scan.incremental.close-idle-reader.enabled | false | 快照完成后关闭空闲 Reader,节省资源 |
| connection.pool.size | 20 | 连接池大小 |
| server-time-zone | - | 数据库时区(避免 8 小时偏移问题) |
启动模式详解
| 模式 | 行为 |
|---|---|
| initial(默认) | 先执行全量快照,再无缝切换到增量 Binlog |
| latest-offset | 跳过全量,从最新 Binlog 位点开始(仅捕获启动后的变更) |
| earliest-offset | 从最早可用 Binlog 位点开始 |
| specific-offset | 从指定 Binlog 位点/GTID 开始 |
| timestamp | 从指定时间戳位点开始 |
| snapshot | 仅执行全量快照,完成后退出 |
5.3 Sink 配置(以 Doris 为例)
yaml
sink:
type: doris
fenodes: 127.0.0.1:8030
username: root
password: ""
table.create.properties.light_schema_change: true
table.create.properties.replication_num: 1
5.4 Route 配置(表路由)
yaml
route:
- source-table: app_db.orders # 精确匹配
sink-table: ods_db.ods_orders
- source-table: app_db.order_\d+ # 正则匹配 - 分表合并
sink-table: ods_db.ods_orders_all
description: "分表合并"
5.5 Transform 配置(数据转换)
less
transform:
- source-table: app_db.users
projection: id, UPPER(name) AS name, age, created_at # 列投影 + 计算列
filter: age > 18 AND status = 'active' # 行过滤
description: "过滤未成年用户"
5.6 Pipeline 全局配置
| 参数 | 默认值 | 说明 |
|---|---|---|
| name | - | 作业名称 |
| parallelism | 1 | 全局并行度 |
| schema.change.behavior | lenient | DDL 变更处理策略 |
| execution.runtime-mode | STREAMING | 运行模式(STREAMING / BATCH) |
| local-time-zone | - | 时区设置 |
5.7 完整配置示例
yaml
source:
type: mysql
hostname: 192.168.1.100
port: 3306
username: cdc_user
password: ${CDC_PASSWORD}
tables: trade_db..*
tables.exclude: trade_db.tmp_.*
server-id: 5400-5404
scan.startup.mode: initial
scan.incremental.snapshot.chunk.size: 16384
scan.snapshot.fetch.size: 2048
scan.incremental.close-idle-reader.enabled: true
connection.pool.size: 30
sink:
type: doris
fenodes: 10.0.0.1:8030
username: doris_admin
password: ${DORIS_PASSWORD}
table.create.properties.light_schema_change: true
table.create.properties.replication_num: 3
route:
- source-table: trade_db.orders
sink-table: ods.ods_orders
- source-table: trade_db.order_detail_\d+
sink-table: ods.ods_order_detail_all
description: "订单明细分表合并"
transform:
- source-table: trade_db.users
projection: id, name, phone, city, created_at
filter: status != 'deleted'
description: "过滤已删除用户,裁剪敏感列"
pipeline:
name: Trade DB Full Sync to Doris
parallelism: 4
schema.change.behavior: try_evolve
local-time-zone: Asia/Shanghai
六、最佳实践
6.1 生产部署建议
资源规划:
- 并行度设置:建议按表数量和吞吐量需求设置,初始全量阶段可适当调高(如 4-8),增量阶段可降低
- 内存配置:TaskManager 建议 4-8GB,大表场景需关注 RocksDB State 大小
- Checkpoint 配置:间隔 1-3 分钟(金融场景 30s),使用增量 Checkpoint + 持久化存储(S3/HDFS)
server-id 配置:
yaml
# 使用范围语法,每个并发分配独立 ID
server-id: 5400-5408 # 如果 parallelism=4,至少需要 4 个 ID
⚠️ 注意:server-id 在 MySQL 集群中必须全局唯一,不能与主从复制的 slave ID 冲突。
时区一致性:
yaml
source:
type: mysql
server-time-zone: Asia/Shanghai # 必须与源库会话时区一致
pipeline:
local-time-zone: Asia/Shanghai
⚠️ 注意:时区不一致是生产中最常见的数据错误来源之一(典型表现为时间偏移 8 小时)。
6.2 性能调优手册
| 调优项 | 建议 | 原理 |
|---|---|---|
| 增大 chunk.size | 8192 → 16384 ~ 65536 | 减少 Chunk 数量,提升全量读取速度 |
| 增大 fetch.size | 1024 → 2048 ~ 4096 | 减少网络往返,提升单 Chunk 读取效率 |
| 开启 close-idle-reader | true | 全量完成后释放空闲 Reader 资源 |
| 合理设置并行度 | 按表数量和下游承载力 | 避免过高导致 Source 连接过多或 Sink 压力过大 |
| Sink 批写配置 | 增大 batch size / flush interval | 减少下游小写入,提升吞吐 |
| 列裁剪 | Transform 中只投影必要列 | 减少网络传输和序列化开销 |
| State Backend | 使用 RocksDB + 增量 Checkpoint | 适应大状态场景 |
6.3 监控关键指标
| 指标 | 关注点 | 告警阈值建议 |
|---|---|---|
| 端到端延迟(E2E Latency) | P95/P99 | > 5s 告警 |
| Backpressure | 定位瓶颈在 Source/Transform/Sink | isBackPressured 持续 true |
| Checkpoint 成功率 | 失败影响恢复能力 | 连续 3 次失败告警 |
| Binlog 位点差值(Record Lag) | 与源端最新位点的差值 | 持续增大告警 |
| 吞吐量 TPS | 异常下降 | 下降 > 30% 告警 |
| Sink 写入错误率 | 唯一键冲突/超时等 | > 0.1% 告警 |
6.4 常见故障排查
| 故障现象 | 原因 | 解决方案 |
|---|---|---|
| server-id xxx is already in use | ID 与其他 Slave 冲突 | 更换唯一的 server-id 范围 |
| 时间错位 8 小时 | 时区配置不一致 | 配置 server-time-zone |
| 全量阶段卡住 | 大表快照/锁等待/带宽不足 | 增大 chunk.size、检查网络 |
| 下游写入报错 | 鉴权/建表失败/唯一键冲突 | 检查 Sink 连接和表结构 |
| Sink 反压严重 | 下游写入能力不足 | 增大 Sink 并发/批写参数 |
| DDL 变更不兼容 | 列类型收窄/重命名 | 使用 try_evolve 或手动处理 |
七、总结与展望
Flink CDC 3.x 代表了实时数据集成领域的一个重要方向------声明式、端到端、去中间件化。它的核心价值在于:
- 通过增量快照算法实现了无锁、并发、可恢复的全增量一体化同步
- 通过 YAML Pipeline 实现了零代码的数据集成体验
- 通过 Schema Evolution 实现了上游 DDL 变更的自动传播
- 作为 Apache Flink 子项目,拥有强大的社区支撑和活跃的迭代节奏
对于技术栈以 Flink 为核心、目标是构建实时数仓/湖仓一体架构的团队,Flink CDC 是目前最值得优先评估的方案。