放弃Canal后,我们用Flink CDC实现了99.99%的数据一致性

对数据的实时性要求越来越高。传统的离线数仓(T+1)已无法满足业务对秒级响应的需求,而实时数仓和数据湖(Data Lake)架构正成为主流。然而,如何将业务数据库中的变更数据(Insert/Update/Delete)低延迟、高可靠、无侵入地同步到下游系统,一直是构建实时链路的关键挑战。

CDC 的全称是 Change Data Capture ,在广义的概念上,只要是能捕获数据变更的技术,我们都可以称之为 CDC 。

又细分为基于直连查询的 CDC基于Binlog的 CDC

以下是之前的mysql binlog日志处理流程,例如canal监听binlog把日志写入到kafka中。 Flink实时消费Kakfa的数据实现mysql数据的同步,整体上可以分为以下几个阶段。

  1. mysql开启binlog

  2. canal同步binlog数据写入到kafka

  3. flink读取kakfa中的binlog数据进行相关的业务处理。

整体的处理链路较长,需要用到的组件也比较多。

Apache Flink CDC可以直接从数据库获取到binlog供下游进行业务计算分析。

也就是说数据不再通过canal与kafka进行同步,而flink直接进行处理mysql的数据。节省了canal与kafka的过程。

Flink CDC(Change Data Capture)应运而生,它基于 Apache Flink 构建,能够直接从数据库的事务日志(如 MySQL 的 binlog、Oracle 的 Redo Log)中捕获数据变更,并以流式方式输出到 Kafka、Pulsar、Iceberg、Hudi 或直接写入实时计算引擎。其核心价值体现在:

  • 无侵入性:无需修改业务代码或添加触发器,仅通过读取数据库日志即可捕获变更。

  • 端到端 Exactly-Once 语义:结合 Flink Checkpoint 机制,保障数据不丢不重。

  • 统一处理模型:CDC 数据以流的形式进入 Flink,可无缝对接窗口计算、维表关联、状态管理等高级功能。

  • 入湖桥梁:作为连接 OLTP 系统与数据湖(如 Iceberg、Delta Lake)的关键组件,实现"实时入湖"(Real-time Lakehouse)。

因此,Flink CDC 堪称是"实时数据入湖的第一公里",是构建现代实时数据架构不可或缺的一环。

核心原理

Flink CDC 的底层依赖于 Debezium(开源 CDC 引擎),通过封装 Debezium 的 Source Connector,将其集成到 Flink 的 SourceFunction 中。其工作流程如下:

  1. 启动时全量快照(Snapshot)

  2. 切换至增量日志(Binlog/Redo Log)

  3. 统一事件格式输出:所有记录(包括快照和增量)都以统一的 RowData 或 JSON 格式输出,包含操作类型(INSERT/UPDATE/DELETE)、时间戳、前后镜像等信息

  4. Checkpoint 保障一致性:

注:Flink CDC 2.0+ 引入了 "无锁快照" 和 "并行读取" 机制,大幅提升大表初始化性能。

接入常见数据库实践

✅ MySQL 通过 Flink 接入

编写一个 Flink CDC 应用程序,以将 MySQL 表更改推送到 Kafka Topic 中。可以使用 Flink 的 flink-connector-jdbc 库和 flink-connector-kafka 库来实现此目的。

核心代码:

复制代码
public static void main(String[] args) throws Exception {
  
    StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

    env.setParallelism(1);

    Properties properties = new Properties();
    properties.setProperty("bootstrap.servers", "localhost:9092");
    properties.setProperty("group.id", "test-group");

    JdbcSource<RowData> source = JdbcSource.<RowData>builder()
            .setDrivername("com.mysql.jdbc.Driver")
            .setDBUrl("jdbc:mysql://localhost:3306/test_db")
            .setUsername("flink_cdc_user")
            .setPassword("password")
            .setQuery("SELECT id, name, age, email FROM test_table")
            .setRowTypeInfo(Types.ROW(Types.INT, Types.STRING, Types.INT, Types.STRING))
            .setFetchSize(1000)
            .build();

    DataStream<RowData> stream = env.addSource(source);

前提条件

  • 开启 binlog:binlog_format=ROW,binlog_row_image=FULL

  • 用户需有 REPLICATION SLAVE, REPLICATION CLIENT, SELECT 权限

一个简单的示例运行及结果:

复制代码
$ bin/flink run -c com.example.MyCDCJob ./my-cdc-job.jar --database.server=mysql.example.com --database.port=3306 --database.name=mydb --database.username=myuser --database.password=mypassword --table.name=mytable --debezium.plugin.name=mysql --debezium.plugin.property.version=1.3.1.Final

可以看到,当有数据变更时,Flink CDC Job 会输出变更的表名、记录的主键以及变更的数据。例如,在这个示例中,有一行记录的年龄字段从25变成了27。

复制代码
[INFO] Starting CDC process for table: mytable.
[INFO] Initializing CDC source...
[INFO] CDC source successfully initialized.
[INFO] Starting CDC source...
[INFO] CDC source successfully started.
[INFO] Adding CDC source to Flink job topology...
[INFO] CDC source successfully added to Flink job topology.
[INFO] Starting Flink job...
[INFO] Flink job started successfully.
[INFO] Change data for table: mytable.
[INFO] Record key: {"id": 1}, record value: {"id": 1, "name": "Alice", "age": 25}.
[INFO] Record key: {"id": 2}, record value: {"id": 2, "name": "Bob", "age": 30}.
[INFO] Record key: {"id": 3}, record value: {"id": 3, "name": "Charlie", "age": 35}.
[INFO] Change data for table: mytable.
[INFO] Record key: {"id": 1}, record value: {"id": 1, "name": "Alice", "age": 27}.

✅ MySQL 通过 Flink SQL 接入 CDC

示例:创建 CDC 源表并写入控制台

复制代码
-- 创建 MySQL CDC 源表
CREATE TABLE mysql_users (
  id INT PRIMARY KEY NOT ENFORCED,
  name STRING,
  email STRING,
  update_time TIMESTAMP(3)
) WITH (
'connector' = 'mysql-cdc',
'hostname' = 'localhost',
'port' = '3306',
'username' = 'flinkuser',
'password' = 'flinkpw',
'database-name' = 'test_db',
'table-name' = 'users'
);

-- 查询并输出
SELECT * FROM mysql_users;

使用中的常见问题

Q1:Flink CDC 和传统 Canal / Maxwell 有什么区别?

  • 集成度:Flink CDC 深度集成 Flink,可直接参与流计算;Canal/Maxwell 通常作为独立服务,需额外接入 Flink。

  • 语义保障:Flink CDC 原生支持 Checkpoint 和 Exactly-Once;Canal 需自行实现位点管理。

  • 全量+增量一体化:Flink CDC 自动切换快照与增量;Canal 仅支持增量。

Q2:Flink CDC 如何实现无锁快照?

Flink CDC 2.0 引入 Chunk-based Snapshot 机制:

  • 将表按主键分片(chunk)

  • 每个 chunk 独立读取,并记录高低水位

  • 读取过程中允许并发写入,通过 binlog 补偿中间变更

  • 最终合并快照与增量,保证一致性

Q3:如何处理 DDL 变更(如加列)?

  • 当前限制:Flink CDC 默认不支持动态 DDL 同步(会报错或忽略)

  • 解决方案:

    • 手动重启作业(适用于低频变更)

    • 使用 Schema Registry + 动态反序列化(如 Avro)

    • 结合 Flink 1.17+ 的 Dynamic Table Options 实现 schema evolution(实验性)

Q4:Flink CDC 能否捕获 DELETE 操作?

可以。DELETE 事件会以 op='d' 形式输出,并包含删除前的完整行数据(前提是数据库日志包含 before image,如 MySQL ROW 格式)。

Q5:如何优化大表 CDC 的性能?

  • 升级到 Flink CDC 2.3+,启用 parallelism 参数

  • 增加 source 并行度(需主键分布均匀)

  • 调整 checkpoint 间隔(避免过频繁影响吞吐)

结语

Flink CDC 正在成为实时数据管道的事实标准。它不仅简化了从数据库到数据湖的同步链路,还为实时分析、实时风控、实时推荐等场景提供了高质量的数据源。随着 Flink 社区对 CDC 的持续投入(如支持更多数据库、增强 schema evolution、提升性能),其在实时数仓架构中的地位将愈发不可替代。

建议学习和实践路径:从 MySQL CDC 入手 → 接入 Kafka → 写入 Iceberg/Hudi /Paimon→ 构建端到端实时入湖 pipeline

相关推荐
云和数据.ChenGuang2 小时前
openEuler安装elasticSearch
大数据·elasticsearch·搜索引擎·全文检索·jenkins
Herlie2 小时前
AI 创业这三年:我的三次认知迭代与自我修正
大数据·人工智能
驾数者2 小时前
Flink SQL自定义函数开发:标量、聚合、表值函数实现
python·sql·flink
PNP Robotics2 小时前
聚焦具身智能,PNP机器人展出力反馈遥操作,VR动作捕捉等方案,获得中国科研贡献奖
大数据·人工智能·python·学习·机器人
木易 士心2 小时前
数字身份的通行证:深入解析单点登录(SSO)的架构与艺术
java·大数据·架构
2401_878820472 小时前
ES知识点二
大数据·elasticsearch·搜索引擎
Jackyzhe2 小时前
Flink源码阅读:Checkpoint机制(下)
大数据·flink
科创致远3 小时前
esop系统可量化 ROI 投资回报率客户案例故事-案例1:宁波某精密制造企业
大数据·人工智能·制造·精益工程
Hello.Reader4 小时前
Flink SQL 的 JAR 语句ADD JAR / SHOW JARS / REMOVE JAR(SQL CLI 实战 + 避坑指南)
sql·flink·jar