没有Kafka怎么办?Flink SQL 创建 mysql-cdc 作业

前言

在大数据与实时计算的浪潮中,如何高效、低延迟地捕获和处理业务数据库的变更日志,已成为构建流式数据管道的关键。MySQL-CDC(Change Data Capture)技术正是为此而生,它能够实时监听MySQL数据库的二进制日志(Binlog),精准捕捉数据的插入、更新和删除等变更事件,并将这些事件作为连续不断的流推送到下游处理系统,为实时数仓、数据同步和流式分析提供了源头活水。

与传统依赖定时查询的JDBC连接方式相比,MySQL-CDC带来了一场范式革命。JDBC作业通常以固定的时间间隔轮询拉取数据,无法感知中间的变化,既存在显著的延迟,也可能因频繁查询而对源库造成压力。而CDC则是基于事件的、真正的流式处理,它毫秒级地响应变化,保证了数据的端到端实时性,并且减少了对源库的侵入性。

因此,借助Flink SQL创建MySQL-CDC作业,开发者可以轻松地将数据库变更流与Kafka、Hudi、Iceberg等数据湖仓无缝集成,广泛应用于实时数仓ETL、物化视图更新、微服务间缓存同步、审计以及在线机器学习特征更新等核心场景,是实现"流批一体"架构不可或缺的利器。

添加依赖

下载后将flink-sql-connector-mysql-cdc-2.4.0.jar包拷贝到 $FLINK_HOME/lib 和 $DINKY_HOME/extends/flink1.17/ 目录下

bash 复制代码
# mysql-cdc连接器,注意版本兼容性
wget https://repo1.maven.org/maven2/com/ververica/flink-sql-connector-mysql-cdc/2.4.0/flink-sql-connector-mysql-cdc-2.4.0.jar

配置为mysql-cdc连接器的源表

sql 复制代码
CREATE TABLE users
(
    `id`                      BIGINT NOT NULL COMMENT '主键id',
    `user_name`               STRING COMMENT '姓名',
    `sex`                     STRING COMMENT '性别',
    `age`                     BIGINT COMMENT '年龄',
    PRIMARY KEY (`id`) NOT ENFORCED
) WITH (
      'connector' = 'mysql-cdc',
      'hostname' = 'xxx.xxx.xxx.xxx',
      'port' = 'xxxx',
      'username' = 'xxxx',
      'password' = 'xxxx',
      'database-name' = 'xxxx',
      'table-name' = 'users',
      'server-time-zone' = 'Asia/Shanghai',
      'scan.startup.mode' = 'initial', -- 先做全量快照,然后从binlog最新位置开始读取(默认)
--       'scan.startup.mode' = 'earliest-offset', -- 直接跳过快照,从binlog最早位置开始读取。
--       'scan.startup.mode' = 'latest-offset',
      'debezium.skipped.operations' = 'd,t' -- 跳过删除/truncate操作
);
  • 'scan.startup.mode' = 'initial'第一次会做全量读取,后续做增量读取;
  • 'debezium.skipped.operations' = 'd,t'表示源表即使做了delete或者truncate操作,目标表也不会同步执行,这是为了防止源表删库跑路牵连到了目标表;

配置为jdbc连接器的目标表

sql 复制代码
CREATE TABLE users2
(
    `id`                      BIGINT NOT NULL COMMENT '主键id',
    `user_name`               STRING COMMENT '姓名',
    `sex`                     STRING COMMENT '性别',
    `age`                     BIGINT COMMENT '年龄',
    PRIMARY KEY (`id`) NOT ENFORCED
) WITH (
      'connector' = 'jdbc',
      'table-name' = 'users2',
      'url' = 'jdbc:mysql://x.x.x.x:3306/xxxx?useSSL=false&serverTimezone=UTC',
      'username' = 'xxxx',
      'password' = 'xxxx',
      'sink.buffer-flush.max-rows' = '100', -- 根据性能调整,小批量写入
      'sink.buffer-flush.interval' = '5s', -- 根据性能调整,控制写入延迟
--     'sink.parallelism' = '4',               -- 增加写入并发
      'sink.max-retries' = '3' -- 写入失败重试
);

最后sink语句

sql 复制代码
insert into user2
select * from users

多个sink表

如果想在一个作业里将源表的数据,链式传输给多个表,例如上文中从users表同步数据到users2表和users3表,就要再增加一个users3表的jdbc映射,以及insert语句。

sql 复制代码
CREATE TABLE users3
(
    `id`                      BIGINT NOT NULL COMMENT '主键id',
    `user_name`               STRING COMMENT '姓名',
    `sex`                     STRING COMMENT '性别',
    `age`                     BIGINT COMMENT '年龄',
    PRIMARY KEY (`id`) NOT ENFORCED
) WITH (
      'connector' = 'jdbc',
      'table-name' = 'users3',
      'url' = 'jdbc:mysql://x.x.x.x:3306/xxxx?useSSL=false&serverTimezone=UTC',
      'username' = 'xxxx',
      'password' = 'xxxx',
      'sink.buffer-flush.max-rows' = '100', -- 根据性能调整,小批量写入
      'sink.buffer-flush.interval' = '5s', -- 根据性能调整,控制写入延迟
--     'sink.parallelism' = '4',               -- 增加写入并发
      'sink.max-retries' = '3' -- 写入失败重试
);

insert into users3
select * from users;

注意这里的源表还是users,而不是users2 ,users2映射的是jdbc连接器,所以如果源表使用users2的话,users3是不会读取到更新记录的。

由于我这里写的场景比较简单,所以insert into users3的代码很少,如果users表到users2和users3表的结构差异比较大,中间做了很多转换,导致insert sql逻辑复杂,那么跳过users2而直接从users表insert into users3时可读性就会很差。解决办法如下:

sql 复制代码
CREATE TABLE users2_cdc
(
    `id`                      BIGINT NOT NULL COMMENT '主键id',
    `user_name`               STRING COMMENT '姓名',
    `sex`                     STRING COMMENT '性别',
    `age`                     BIGINT COMMENT '年龄',
    PRIMARY KEY (`id`) NOT ENFORCED
) WITH (
      'connector' = 'mysql-cdc',
      'hostname' = 'xxx.xxx.xxx.xxx',
      'port' = 'xxxx',
      'username' = 'xxxx',
      'password' = 'xxxx',
      'database-name' = 'xxxx',
      'table-name' = 'users2',
      'server-time-zone' = 'Asia/Shanghai',
      'scan.startup.mode' = 'initial', -- 先做全量快照,然后从binlog最新位置开始读取(默认)
--       'scan.startup.mode' = 'earliest-offset', -- 直接跳过快照,从binlog最早位置开始读取。
--       'scan.startup.mode' = 'latest-offset',
      'debezium.skipped.operations' = 'd,t' -- 跳过删除/truncate操作
);

insert into users3
select * from users2_cdc;

users2_cdc这个表要使用mysql-cdc连接器,虽然它也是users2表,但是和前文中创建的使用jdbc连接器的users2表要分别创建。以上的两条链路也可以分别写在不同的Flink SQL作业里。

参考

【flinkCDC】Cannot read the binlog filename and position via 'SHOW MASTER STATUS'_caused by: org.apache.flink.util.flinkruntimeexcep-CSDN博客

Linux mysql5.7开启 binlog_liunx mysql查看binlog是否开启-CSDN博客

相关推荐
高频因子挖掘机29 分钟前
本地已有一年历史数据,第二天更新别只拉“昨天一天”:量化 K 线增量同步实践
后端·github·api
高频因子挖掘机43 分钟前
只补缺失日期,不重拉全部历史:增量更新 K 线应该怎么设计?
后端·github·api
Gopher_HBo1 小时前
Go reflect 包动态调用与方法反射 —Call、MakeFunc 与性能
后端
Elastic 中国社区官方博客1 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
商业看点解说2 小时前
支持私有化部署的 AI 助手有哪些?把“私有化“拆成四层,排除一类常被错放的产品
大数据·人工智能
码事漫谈2 小时前
人设带处境,AI 更入戏
后端
清华kenny2 小时前
安徽晓得养HIS系统适合哪类健康机构升级使用
大数据·人工智能·安徽晓得养
掘金酱2 小时前
【10.24 特别活动】AI 时代,程序员的新饭碗在哪里?
前端·人工智能·后端
leeyi2 小时前
缓存同步之后,还要考虑没收到消息的节点
后端·性能优化·erlang
SelectDB3 小时前
数仓 TCO 怎么算:五项成本拆解框架速查
大数据·数据库·开源