news 2026/10/26 1:54:30

Flink CDC 3.0:MySQL 到 Doris 实时同步与整库接入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flink CDC 3.0:MySQL 到 Doris 实时同步与整库接入

简介:《Flink CDC 3.0:MySQL数据实时捕获与同步至Doris的全流程解析及应用案例》由尚硅谷研究院编写,面向具备Flink基础的大数据研发与实时同步工程师,帮助读者理解CDC原理并落地MySQL到Doris的Streaming ETL。文档先厘清变更数据捕获概念,对比基于查询与基于Binlog方案在延迟、完整性和数据库压力上的差异,再说明flink-cdc-connectors组件的能力。实操从MySQL建库建表与数据准备讲起,覆盖DataStream和Flink SQL两种方式,并完整演示MySQL至Doris链路的环境搭建、配置编写、任务启动与测试,适合需要打通实时入仓链路、验证数据一致性及排查同步延迟的读者。压缩包共1个docx文件,约145KB,按CDC简介与案例实操两章组织,兼顾原理与工程实践,结构紧凑便于边读边练。已有1789人学习,可作为掌握开启Binlog、设置检查点等配置细节与排错思路的参考。

1. 从一张订单表说起:MySQL 到 Doris 的实时同步到底难在哪

Flink CDC 3.0 这套东西,我最开始接触是因为一个很具体的麻烦:业务方要求 MySQL 里刚改的订单状态,五分钟内必须在 Doris 的报表里看到。批处理顶不住这种需求,一条 StreamingETL 链路就得自己搭,而 Flink CDC 是这条链路上最省事的一环——不用额外部署采集组件、不用维护消息队列做中转、不用自己解析 Binlog 日志格式。它把 MySQL 的增量变更直接变成 Flink 里的一条流,再往 Doris 里写,全量和增量一次搞定,断点续传靠 Checkpoint 兜底。

这份资料的价值在于它把三件事串成了一条完整的线:CDC 的原理与两种执行模式的取舍、DataStream 和 Flink SQL 两种编码方式怎么接 MySQL、以及 3.0 新增的 YAML Pipeline 怎么把 MySQL 整库同步到 Doris。适合已经能跑起一个 Flink 作业、但还没真正在生产链路上趟过实时同步坑的开发者。下面按「选型 → 环境 → 编码 → 避坑 → 改造」的顺序拆。

2. 选型与架构拆解:查询式 CDC 与 Binlog 式 CDC 差在哪条链路上

2.1 一张对比表背后的执行模式差异

CDC 全称 Change Data Capture,核心动作只有一句话:盯住数据库的变动(insert / update / delete),按发生顺序记下来,喂给下游消费。听起来简单,但「怎么盯」这件事决定了整条链路的延迟、数据库压力和完整性。

传统做法是基于查询:定时跑一条select * from t where update_time > 上次时间,捞增量。Sqoop、DataX 走的就是这个路子,属于 Batch 模式。它的硬伤有三个——删不掉的数据它看不见(物理删除在源表里已经没了)、更新延迟取决于轮询间隔、每次全表扫都给源库加一份压力。

另一种是基于 Binlog:MySQL 把每次数据变更写进二进制日志,采集端伪装成从库去订阅这份日志。Canal、Maxwell、Debezium 都是这个思路,属于 Streaming 模式。它能拿到完整的 before/after 镜像,包括删除操作,延迟在毫秒到秒级,而且读 Binlog 对业务库几乎无感。

维度基于查询的 CDC基于 Binlog 的 CDC
代表产品Sqoop、DataXCanal、Maxwell、Debezium、Flink CDC
执行模式BatchStreaming
能否捕获全部变更否,删除和中间态会丢是,含 before/after 镜像
变化延迟高,取决于轮询周期低,接近实时
对源库压力有,周期性全表或范围扫描低,读 Binlog 流
断点续传靠水位字段,容易漏靠位点(offset/binlog position)

选型结论很直接:只要下游对时效的要求是分钟级以内,或者业务表存在物理删除,就不该在查询式 CDC 上纠缠。剩下的问题只是「用哪个 Binlog 采集器」,这就是 Flink CDC 要回答的。

2.2 flink-cdc-connectors 在整条链路里的位置

Flink CDC 不是一个独立服务,它是 Flink 的一个 Source 组件。内部用 Debezium 做 Binlog 解析引擎,外面套一层 Flink 的 Source API,把解析出来的变更事件变成 Flink 数据流里的记录。这样做的好处是省掉了「采集器 → 消息队列 → 计算引擎」这条经典三段式链路里的中间环节,采集和计算在同一个作业里完成。

关键设计在状态管理上。作业启动时先做一次全量快照(snapshot),把当前表里的存量数据读出来;快照读完的位置和之后的 Binlog 位点,都会作为算子状态参与 Flink 的 Checkpoint。所以恢复的时候不是「从头再来」,而是接着上次的位置继续读。这也是为什么后面代码里 Checkpoint 必须开——不开 Checkpoint,这套断点续传的能力等于没有。

从数据形态看,MySqlSource拿到的是 Debezium 的SourceRecord,需要反序列化成下游能认的格式。最省事的是JsonDebeziumDeserializationSchema,直接输出 JSON 字符串,带op字段标识操作类型(c 插入、u 更新、d 删除、r 快照读)。要落库到 Doris,通常还得在中间做一层解析,或者干脆走 3.0 的 Pipeline 让它内置处理。

2.3 3.0 的 Pipeline 模型:把 Source 和 Sink 装进一个 YAML

3.0 最大的变化是引入了 Pipeline 这个概念,把「采集端」和「写入端」拆成独立的 connector jar,再用一份 YAML 把它们装配起来。作业不再需要你写 Java 代码,flink-cdc.sh读配置文件、拼出 Flink 作业图、提交到集群。

这个模型里有三个角色:source connector 负责读源库(MySQL),sink connector 负责写目标端(Doris、Kafka、Paimon 等),pipeline 负责中间的 schema 演进、表名路由和数据转换。这样拆的收益是可组合——同一份源配置,换一个 sink 配置就能写到不同目标,不用改代码;代价是灵活性受限于 YAML 支持的选项,复杂的业务转换还是得回到 DataStream。

三种方式的定位可以这么理解:DataStream 最灵活,适合要做复杂处理或自定义序列化的场景;Flink SQL 最轻,适合快速验证和简单同步;YAML Pipeline 最工程化,适合整库同步这种配置量大的活儿。后面三种都会过一遍,按你的场景挑。

3. 环境准备:MySQL Binlog 开关与 Doris 集群的一次性改造

3.1 建库建表与造一批能验证增删改的数据

先准备两个库:一个test做主同步,一个test_route专门验证路由映射。每个库里建三张结构简单的表,主键用字符串方便手工造数。这套数据虽然简单,但足够覆盖后面要验证的三种操作:新增、更新、删除。

-- 主同步库 CREATE DATABASE IF NOT EXISTS test; USE test; CREATE TABLE t1 ( id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) ); CREATE TABLE t2 ( id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) ); -- t3 故意用不同的列名和类型,用来验证 schema 演进 CREATE TABLE t3 ( id VARCHAR(255) PRIMARY KEY, sex VARCHAR(255) ); -- 路由验证库 CREATE DATABASE IF NOT EXISTS test_route; USE test_route; CREATE TABLE t1 (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255)); CREATE TABLE t2 (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255)); CREATE TABLE t3 (id VARCHAR(255) PRIMARY KEY, sex VARCHAR(255));

接着灌初始数据。注意test和test_route里的 t1、t2 主键故意错开,后面看 Doris 里的结果时,一眼就能分辨出哪条记录是从哪个库同步过去的。

USE test; INSERT INTO t1 VALUES ('1001','u001'),('1002','u002'),('1003','u003'); INSERT INTO t2 VALUES ('1001','u001'),('1002','u002'),('1003','u003'); INSERT INTO t3 VALUES ('1001','F'),('1002','F'),('1003','M'); USE test_route; INSERT INTO t1 VALUES ('1001','u001'),('1002','u002'),('1003','u003'); INSERT INTO t2 VALUES ('1004','u004'),('1005','u005'),('1006','u006'); INSERT INTO t3 VALUES ('1001','F'),('1002','F'),('1003','M');

建表和造数这两步看着琐碎,但后面的所有验证都依赖它。特别是test_route.t2用了 1004-1006 这组主键,当你看到 Doris 里doris_test_route.t2出现这三条记录时,就能确认 route 规则确实生效了,而不是碰巧同步到了一个同名表。

3.2 Binlog 参数:四个必须改对的地方

MySQL 8.0 默认关闭 Binlog,这一项不改,Flink CDC 启动时会直接报「无法读取 binlog」之类的错。编辑my.cnf(不同发行版路径可能是/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf),在[mysqld]段落下加配置。

[mysqld] # 集群内唯一即可,CDC 会用它伪装成从库,重复会顶掉别的从库 server-id = 1 # 开启 binlog 并指定文件前缀,实际文件是 mysql-bin.000001 这种 log-bin = mysql-bin # 必须是 row,statement/mixed 拿不到 before/after 镜像,Debezium 直接解析不了 binlog_format = row # 只记录这两个库,减少日志量;不写则全库记录 binlog-do-db = test binlog-do-db = test_route

改完systemctl restart mysqld,用show variables like 'binlog_format'和show master status各确认一次。前者必须是ROW,后者要能看到正在写的 binlog 文件名。

四个参数里最容易翻车的是binlog-do-db。它的语义是「按当前默认库判断」,如果一条 SQL 先use test再写test_route.t1,这条变更不会被记录。所以要么在连接层面强制指定库,要么干脆去掉这个限制、全库开 Binlog,用binlog-ignore-db排除系统库反而更稳。

注意:binlog_format改成row之后,单条变更的日志体积会比 statement 大不少。大表批量更新的场景下要留意磁盘水位,必要时配合binlog_expire_logs_seconds缩短保留时间。

3.3 Doris 侧:FE/BE 起起来,建库属性别忘

Doris 这边要起两个角色:FE 负责元数据和查询规划,BE 负责实际存储。先bin/start_fe.sh --daemon起 FE,再用 MySQL 客户端连上 FE 的 9030 端口执行ALTER SYSTEM ADD BACKEND "node02:9050"把 BE 挂进来,然后bin/start_be.sh --daemon。BE 加不进去,后面建表会卡在副本数不足。

# 通过 MySQL 协议连 FE,端口是 9030 不是 3306 mysql -uroot -P9030 -hnode01 # 建目标库,名字故意和源库区分开,方便验证 route CREATE DATABASE IF NOT EXISTS test; CREATE DATABASE IF NOT EXISTS doris_test_route;

建表这一步交给 Flink CDC 自动做,但有个前提:Doris 侧建表时副本数默认是 3,单机或两节点环境会一直处于副本不健康状态,写入直接失败。这就是后面 YAML 里table.create.properties.replication_num: 1的由来——环境有几个 BE,就设几(不超过)。同理light_schema_change要设为 true,否则源表加列时 Doris 这边会走重量级 Schema Change,同步任务会长时间阻塞。

3.4 Flink 侧:Checkpoint 与 connector jar 都要到位

Flink 1.18 的集群配置里要打开 Checkpoint 间隔,否则 Pipeline 模式的作业根本提交不上去——3.0 的 Pipeline 内部依赖 Checkpoint 做全量到增量的阶段切换。

# conf/flink-conf.yaml execution.checkpointing.interval: 5000

然后是 connector 的 jar。Pipeline 模式下,flink-cdc-pipeline-connector-mysql-3.0.0.jar和flink-cdc-pipeline-connector-doris-3.0.0.jar都要放进$FLINK_HOME/lib下。放错位置(比如放进flink-cdc-3.0.0/lib)是最常见的启动失败原因,因为最终拉起作业的是 Flink 集群本身,它只认自己 lib 下的包。

4. 三种接入方式:DataStream、Flink SQL 与 YAML Pipeline 落地 MySQL→Doris

4.1 Maven 依赖与打包插件

DataStream 方式需要一套完整依赖。Flink 相关的五个包版本统一用 1.18.0,CDC connector 单独用 3.0.0,MySQL 驱动用 8.0.31 和源库版本对齐。

<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <flink.version>1.18.0</flink.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Flink 运行时与 Table API,SQL 方式同样依赖这几个 --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-streaming-java</artifactId> <version>${flink.version}</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-clients</artifactId> <version>${flink.version}</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-table-planner_2.12</artifactId> <version>${flink.version}</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-table-runtime</artifactId> <version>${flink.version}</version> </dependency> <!-- CDC 核心包,DataStream 和 SQL 都靠它 --> <dependency> <groupId>com.ververica</groupId> <artifactId>flink-connector-mysql-cdc</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.31</version> </dependency> </dependencies> <build> <plugins> <!-- 打成带依赖的胖包,否则集群上找不到 CDC 的类 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.0.0</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals><goal>single</goal></goals> </execution> </executions> </plugin> </plugins> </build>

几个参数值得说明。flink-table-planner_2.12的后缀是 Scala 版本,必须和集群用的 Flink 安装包一致(Flink 1.18 默认是 2.12),版本对不上会在作业提交时抛NoSuchMethodError,报错信息完全看不出是 Scala 版本问题——这类问题后面避坑章节还会提到。maven-assembly-plugin的jar-with-dependencies会生成xxx-jar-with-dependencies.jar,提交作业时用的就是它,不是默认的瘦包。

4.2 DataStream 主程序:六步走完一个 CDC 作业

主程序的结构很固定,六个 TODO 分别是环境、Checkpoint、Source、DataStream、Sink、执行。中间 Checkpoint 那段配置最多,也最容易被略过,但恰恰是它决定了作业能不能断点续传。

public class FlinkCDCDataStreamTest { public static void main(String[] args) throws Exception { // 1. 流处理环境,并行度先给 1,方便看日志 StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // 2. Checkpoint:CDC 的 binlog 位点存在这里,不开等于没有断点续传 env.enableCheckpointing(3000L, CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().setCheckpointTimeout(60 * 1000L); env.getCheckpointConfig().setMinPauseBetweenCheckpoints(3000L); // 任务 cancel 后保留最后一次 CK,便于手工恢复 env.getCheckpointConfig().enableExternalizedCheckpoints( CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION); // 故障重启策略:1 分钟内最多失败 3 次 env.setRestartStrategy(RestartStrategies.failureRateRestart( 3, Time.days(1L), Time.minutes(1L))); // 状态后端与存储位置 env.setStateBackend(new HashMapStateBackend()); env.getCheckpointConfig().setCheckpointStorage("hdfs://node01:8020/flink-cdc/ck"); System.setProperty("HADOOP_USER_NAME", "flink"); // 3. 构建 MySQL Source MySqlSource<String> mySqlSource = MySqlSource.<String>builder() .hostname("node03") .port(3306) .databaseList("test") .tableList("test.t1") .username("root") .password("Mysql@123") .deserializer(new JsonDebeziumDeserializationSchema()) .startupOptions(StartupOptions.initial()) .build(); // 4. 从 Source 生成数据流 DataStreamSource<String> mysqlDS = env.fromSource( mySqlSource, WatermarkStrategy.noWatermarks(), "MySQLSource"); // 5. 输出,生产上换成 Doris/Kafka Sink mysqlDS.print(); // 6. 执行 env.execute("flink-cdc-mysql-test"); } }

逐段说明。enableCheckpointing(3000L, EXACTLY_ONCE)里的 3000 是毫秒,作业跑起来后每 3 秒做一次快照,间隔太大会让恢复时重放的数据变多,太小则给 HDFS 造成压力,5 秒左右是个折中值。RETAIN_ON_CANCELLATION很关键——手工 cancel 作业后,如果不保留最后一次 Checkpoint,重启时只能从头做全量,几十 G 的表会直接把你按在地上。HADOOP_USER_NAME这行是给 HDFS 写权限用的,容器里跑可以换成 Kerberos 认证。

MySqlSource的 builder 里,databaseList和tableList都支持正则,test\\.t.*这种写法可以把整个库的表都纳进来。startupOptions是语义最重的参数,取值直接决定作业第一次启动时的行为:

取值首次启动行为适用场景
initial()先做全量快照,再接着读最新 binlog首次接入,要历史数据
earliest()不做快照,从 binlog 最早位置读binlog 保留期内能覆盖全部历史
latest()不做快照,从 binlog 末尾读只要增量,不要历史
specificOffset()从指定 binlog 位点读手工接续某个断点
timestamp()从指定时间戳开始读,更早的事件丢弃按时间补数

initial()是最常用的,但要注意它做快照时会短暂给源库加读锁(Flink CDC 3.0 默认用无锁算法读快照,但如果表没有主键或用了某些配置,会退化到加锁模式),业务高峰期接入需要提前评估。

4.3 提交、Savepoint 与从检查点恢复

打包后上传到集群,用flink run提交。注意-c后面跟的是完整类名。

# 提交作业 bin/flink run -m node01:8081 -c com.demo.cdc.FlinkCDCDataStreamTest \ ./flink-cdc-test.jar # 手工打一个 Savepoint,路径指向 HDFS bin/flink savepoint <JobId> hdfs://node01:8020/flink-cdc/save # 在 WebUI 里 cancel 作业,然后在 MySQL 里做增删改 # 从 Savepoint 恢复 bin/flink run -s hdfs://node01:8020/flink-cdc/save/savepoint-xxxxxx \ -c com.demo.cdc.FlinkCDCDataStreamTest ./flink-cdc-test.jar

验证恢复是否真的生效,看 TaskManager 日志里有没有重新做全量快照。如果日志显示直接进入 Binlog 读取阶段,说明位点恢复成功;如果又开始刷Snapshot相关日志,那就是位点没接上,多半是 Checkpoint 目录没权限或者 Savepoint 路径写错了。这一步我见过不止一次「以为恢复了、其实重跑了全量」的情况,日志一定要翻。

4.4 Flink SQL 写法与取舍

同样的需求用 SQL 表达,代码量能压到二十行以内。核心是一张带connector = 'mysql-cdc'的建表语句。

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); env.enableCheckpointing(3000L); // SQL 方式同样必须开 CK StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env); tableEnv.executeSql( "CREATE TABLE t1 (\n" + " id STRING PRIMARY KEY NOT ENFORCED,\n" + " name STRING\n" + ") WITH (\n" + " 'connector' = 'mysql-cdc',\n" + " 'hostname' = 'node03',\n" + " 'port' = '3306',\n" + " 'username' = 'root',\n" + " 'password' = 'Mysql@123',\n" + " 'database-name' = 'test',\n" + " 'table-name' = 't1'\n" + ")"); tableEnv.sqlQuery("SELECT * FROM t1").execute().print();

PRIMARY KEY NOT ENFORCED这句不能省,Flink SQL 需要靠主键信息判断 upsert 语义,缺少它会退化成 append 模式,更新和删除都表达不了。connector名称在 3.0 里仍然是mysql-cdc,没有随大版本改名,这点容易记错。

SQL 方式的取舍很清楚:写起来快,适合做字段裁剪、简单过滤、多表 join 这类纯 SQL 能表达的转换;但一旦要做复杂序列化、调用外部服务补数据,就得退回 DataStream。另外 SQL 方式在 3.0 里仍然是「单表配置」,整库同步要写一堆建表语句,这时候就该上 Pipeline 了。

4.5 YAML Pipeline:MySQL 整库同步到 Doris 的完整配置

3.0 的 Pipeline 模式把整库同步变成了改配置文件。在 Flink CDC 安装目录下建一个job文件夹,写一份mysql-to-doris.yaml。

source: type: mysql hostname: node03 port: 3306 username: root password: "Mysql@123" # 正则匹配整库,注意反斜杠转义 tables: test\.* # 伪装成从库的 server-id 区间,不能和已有从库冲突 server-id: 5400-5404 server-time-zone: UTC+8 sink: type: doris fenodes: node01:8030 username: root password: "Mysql@123" # 源表加列时走轻量 schema change,不阻塞写入 table.create.properties.light_schema_change: true # 单 BE 环境必须改成 1,否则副本不健康 table.create.properties.replication_num: 1 pipeline: name: Sync MySQL Database to Doris parallelism: 1

配置分三块。source里的tables是整库同步的核心,test\.*匹配test库下所有表,注意点号要转义,写成test.*会被当成正则通配。server-id给一段范围而不是单值,是因为每张表会分配一个独立的读取实例,范围太小会报「server id 被占用」。server-time-zone要和源库时区一致,不一致会导致时间类型字段偏移 8 小时,这个坑很隐蔽。

sink里的fenodes指的是 Doris FE 的 HTTP 端口 8030,不是 MySQL 协议端口 9030,写错会报连接超时。parallelism在 pipeline 层指定,比在 Flink 集群层面调更精细。

启动命令只有一行,前提是 Flink 集群的 Checkpoint 已经配好、connector jar 已经放进 lib。

# 提交 pipeline 作业 bin/flink-cdc.sh job/mysql-to-doris.yaml

作业跑起来后,去 Doris 里show tables from test应该能看到 t1、t2、t3 三张表被自动创建,数据也已经灌进去。接着在 MySQL 里对 t1 做一次 update、对 t2 做一次 delete、给 t3 加一列,再刷新 Doris,这三次操作分别验证了更新同步、删除同步和表结构变更同步。

4.6 路由变更:把 test_route 落到 doris_test_route

默认情况下 Pipeline 会保持库名表名一致地同步过去。要把test_route库落到doris_test_route,靠 route 规则。

source: type: mysql hostname: node03 port: 3306 username: root password: "Mysql@123" tables: test_route\.* server-id: 5400-5404 server-time-zone: UTC+8 sink: type: doris fenodes: node01:8030 username: root password: "Mysql@123" table.create.properties.light_schema_change: true table.create.properties.replication_num: 1 route: # 源表 -> 目标表,支持正则 - source-table: test_route\.t.* sink-table: doris_test_route.t.* pipeline: name: Route MySQL to Doris parallelism: 1

route段的source-table和sink-table都支持正则表达式,写test_route\.t.*到doris_test_route.t.*表示「库内所有 t 开头的表,平移到另一个库」。验证方式很简单:同步完成后 Doris 的doris_test_route.t2里应该有 1004、1005、1006 三条,而test.t2里没有——因为这条数据只存在于test_route库。

路由的价值在于做环境隔离和多租户分流:不同业务库同步到 Doris 的不同 database,或者把分库分表的多张源表汇聚成一张宽表。

5. 避坑与排查:这五处配置不改,任务跑不过十分钟

5.1 启动阶段:连接、类缺失与权限

现象:作业提交后立刻失败,日志里是java.lang.ClassNotFoundException: com.ververica.cdc.connectors.mysql.source.MySqlSource。原因:打的是瘦包,或者 connector jar 没放进$FLINK_HOME/lib。Pipeline 模式下最常见——把flink-cdc-pipeline-connector-*.jar放进了 Flink CDC 自己的 lib 目录,但真正拉起作业的是 Flink 集群。解决:DataStream 用jar-with-dependencies打包;Pipeline 把两个 pipeline connector jar 复制到$FLINK_HOME/lib,重启集群。

现象:报Access denied for user,或者能连上但读不到数据。原因:MySQL 用户没有REPLICATION SLAVE和REPLICATION CLIENT权限。CDC 需要伪装成从库读 Binlog,普通账号不够。解决:GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'user'@'%'; FLUSH PRIVILEGES;。另外 MySQL 8.0 默认用caching_sha2_password,某些老驱动不支持,必要时间改成mysql_native_password。

现象:报no viable alternative at input或者连不上 FE。原因:Doris 的fenodes写成了 9030。8030 是 FE 的 HTTP 端口,9030 是 MySQL 协议端口,sink 用的是前者。解决:改成node01:8030,多 FE 用逗号分隔。

5.2 运行阶段:Checkpoint、重复消费与写入失败

现象:作业跑着跑着 Checkpoint 全部超时,最后触发重启。原因:状态存储指向 HDFS 但没配HADOOP_USER_NAME,或者 Checkpoint 目录权限不对;另一种是全量阶段数据量大,快照做的 barrier 对齐被拖慢。解决:先hdfs dfs -chmod -R 777 /flink-cdc/ck排除权限问题;再把execution.checkpointing.timeout从默认 10 分钟适当放大;超大表分批接入,不要一次同步几十张。

现象:Doris 里出现重复数据,或者主键重复报错。原因:源表没有主键,Pipeline 无法生成 upsert 语义,只能按 append 写入;或者 sink 端的表模型和源表不匹配。解决:优先给源表加主键;实在加不了,在 Doris 侧建 Unique Key 模型表并手工指定 key 列,同时评估数据去重策略。

现象:源表加了一列,Doris 同步任务直接卡住或者报 schema 不兼容。原因:light_schema_change没开,Doris 走的是重量级 Schema Change,期间表不可写,同步任务阻塞。解决:建表时就把table.create.properties.light_schema_change: true加上。已经建了表的,手工执行ALTER TABLE ... SET ("light_schema_change" = "true")后重新提交。加列操作本身的类型也要兼容,源表从VARCHAR(255)扩到VARCHAR(512)一般没问题,从INT改VARCHAR这种就会被拒。

注意:这三个运行期问题的共同点是「日志里能看到,但报错信息指向性很弱」。排查习惯是先把 Flink WebUI 的 Checkpoint 页面打开,看是超时还是失败,再去看 TaskManager 日志里具体的 exception,别一上来就怀疑配置写错。

6. 二次开发:把 demo 管道改成能上生产的形态

跑通 demo 和能上生产之间还隔着一层工程化改造。我一般按这几个方向动手。

参数外置。不要把主机名密码硬编码在 YAML 里。写一个小脚本,用环境变量渲染模板生成最终 job 文件,敏感信息走配置中心或启动时注入。

#!/usr/bin/env bash # gen_job.sh —— 从模板渲染出实际 job 文件 TEMPLATE=job/mysql-to-doris.yaml.tpl OUT=job/mysql-to-doris.yaml sed -e "s|__SRC_HOST__|${SRC_HOST}|g" \ -e "s|__SRC_PWD__|${SRC_PWD}|g" \ -e "s|__DORIS_FE__|${DORIS_FE}|g" \ -e "s|__DB__|${DB_NAME}|g" \ "$TEMPLATE" > "$OUT" # 提交 $FLINK_CDC_HOME/bin/flink-cdc.sh "$OUT"

sed替换是最轻的做法,模板里把可变项写成__SRC_HOST__这类占位符。这样一套模板能生成 dev / staging / prod 三份配置,避免「改了测试环境忘了改生产」这类事故——我真见过因为一份 YAML 里replication_num忘了改,生产 Doris 建了一批副本不健康的表,查了半天才发现。

多表模板化与批量接入。整库同步的表数量上去之后,server-id范围要跟着扩,按「表数 + 2」预留。另外不是所有表都适合实时同步,日志表、流水表这类只增不改的,可以拆到另一条并行度更高的管道里,避免拖慢主链路的 Checkpoint。

监控埋点。Pipeline 模式没有自己的指标接口,只能从 Flink 侧拿。最低成本的方案是定时拉 Flink REST API 的/jobs/:jobid/checkpoints,看最近一次 Checkpoint 的持续时间和大小,持续变大说明有反压或者状态泄漏。再加一个数据一致性对账脚本,每天跑一次源表与目标表的行数和主键集合比对,比盯着 Checkpoint 更实际。

改造点做法验证方式
配置外置模板 + 环境变量渲染用测试环境变量跑一遍,确认无硬编码
敏感信息启动时注入,不落盘grep -r password job/应无结果
多表接入正则 + route 映射抽样比对源表与目标表行数
状态监控Flink REST API 拉 CK 指标连续 24 小时 CK 无超时
数据对账每日主键集合比对差异率为 0 才认为链路健康

验证方法上,我更信「破坏性测试」:故意在同步过程中 kill 掉 TaskManager,看作业能不能自己从 Checkpoint 恢复并且不丢不重。这个测试过不了,其他指标再好看都是纸面功夫。EXACTLY_ONCE 只覆盖到 Flink 内部状态,Sink 端要真正实现精确一次,还得靠 Doris 的 Stream Load 事务标签去重,这一点在跨版本升级时要特别留意。

从那以后我每次接一条新的实时同步链路,都会先把「全量阶段暂停、增量阶段 kill 一次、加列一次」这三件事走一遍,再谈上线。这三步能挡住八成的配置问题。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 1:53:38

具身智能协同演化动力学(118):具身智能的认知桥梁与文化基因详解

前沿技术探索:FRA核心定义与设计理念 FRA(Factorized Robotic Agent, FRA,亦称:因式分解机器人智能体)是一套因式化数实融合一体化新型智能系统,核心设计理念是全链路系统解耦,将机器人作业的感知、推理、动作执行三大核心能力拆解为独立平行子模块,整合为统一、闭环、…

作者头像 李华
网站建设 2026/10/26 1:53:20

体素越小越好?用一份图质报告读懂 CBCT 的取舍

“这台能做到更小体素&#xff0c;所以一定更好。”这是最容易出现在参数比较中的推断之一&#xff0c;也最值得停下来复核。体素规定输出网格&#xff0c;图像质量描述系统在一定条件下传递信息的能力&#xff1b;后者要看任务&#xff0c;也要看噪声、模糊、偏差和伪影。 本…

作者头像 李华
网站建设 2026/10/26 1:53:10

拆开一台 CBCT:球管、平板探测器与旋转机架如何配合

如果把一台CBCT拆成采购清单&#xff0c;你会得到球管、高压发生器、探测器、电机、编码器、控制板和工作站。如果把它拆成系统&#xff0c;你得到的是另一张图&#xff1a;在已知几何位置&#xff0c;以受控的时序获取可解释的投影&#xff0c;同时保证安全状态、热状态和数据…

作者头像 李华
网站建设 2026/10/26 1:52:59

【无标题】注意

### 标识符规则&#xff1a;字母、数字、_、$&#xff1b;**不能以数字开头&#xff0c;不能是关键字&#xff0c;区分大小写** 命名规范&#xff1a;- 类名&#xff1a;大驼峰 HelloWorld - 变量 / 方法&#xff1a;小驼峰 studentName - 常量&#xff1a;全大写&#xff0c;…

作者头像 李华
网站建设 2026/10/26 1:52:58

Linux FFmpeg深度解析之avformat_find_stream_info调用流程与实战(二)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》作者 博主新书推荐&#xff1a;《Android系统多媒体进阶实战》&#x1f680; Android Audio工程师专栏地址&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; Android多媒体专栏地址&a…

作者头像 李华