一、flink cdc同步数据问题抛出
在使用 Flink CDC 将 MySQL 数据实时同步到 StarRocks 的过程中,你是否遇到过这样的问题?
源库加了一个新字段,Flink CDC 任务突然报错中断,日志显示:
Caused by: java.sql.SQLException: ddl sql: ALTER TABLE
t_xxxADD COLUMNnew_fieldVARCHAR(32) ...Column 'xxx.new_field' does not exist
这直接导致flink任务中断,数据同步停滞,告警拉满。
二、根本原因分析
1. Flink CDC 默认会同步 DDL
Flink CDC(基于 Debezium)不仅捕获数据变更(INSERT/UPDATE/DELETE),还会捕获 DDL 事件(如 ALTER TABLE)。
2. StarRocks 不支持自动执行 DDL
StarRocks 是 OLAP 数据库,不支持通过 JDBC 自动执行 ALTER TABLE。当 Flink 尝试将源库的 DDL 推送到 StarRocks 时,由于目标表没有该字段,直接报错。
3. 任务设计缺陷:未隔离元数据与数据同步
很多团队误以为"CDC 能自动搞定一切",但实际上:
Flink CDC 应只负责数据同步,表结构应由 DBA 手动维护。
4. Schema 变更场景总结
| 场景 | 原因 |
|---|---|
| 源库加字段 | Flink CDC 捕获 DDL → 尝试在下游执行 ALTER TABLE → 下游不支持(如 StarRocks)→ 报错 |
| 源库删字段 | Flink 读取的 Binlog 记录包含旧字段 → 反序列化失败(字段不存在)→ 任务崩溃 |
| 字段类型变更 | 如 VARCHAR(50) → INT,Flink 类型推断失败或写入时类型不匹配 → 异常 |
| 未开启 Schema Evolution | 下游存储(如 Kafka + Avro)未启用兼容性策略 → 消费者无法解析新 Schema |
核心原因:Flink 默认假设 Schema 是静态的,一旦变化,若无容错机制,就会失败
三、解决方案(生产级推荐)
方案一:【立即修复】禁用 DDL 自动同步
最简单有效的方式:关闭 Flink CDC 的 DDL 处理能力,仅同步数据。
修改 Flink CDC Source 配置:
java
import com.ververica.cdc.connectors.mysql.source.MySqlSource;
MySqlSource<String> source = MySqlSource.<String>builder()
.hostname("your-mysql-host")
.port(3306)
.databaseList("calcdb")
.tableList("calcdb.t_bill_center_send")
.username("cdc_user")
.password("cdc_pass")
//关键配置:禁止处理 Schema 变更(DDL)
.schemaChangeMode(MySqlSource.SchemaChangeMode.NONE)
.deserializer(new JsonDebeziumDeserializationSchema())
.build();
SchemaChangeMode.NONE:完全忽略 DDL 事件,只处理数据变更。
此时,flink任务不再因加字段而崩溃。
方案二:【手动补救】先改 StarRocks 表结构
在禁用 DDL 后,必须手动在 StarRocks 中添加对应字段,否则新数据写入会失败(字段数不匹配)。
sql
-- 在 StarRocks 中执行
ALTER TABLE t_bill_center_send
ADD COLUMN dispatch_site_code_ewb VARCHAR(32) DEFAULT NULL COMMENT '派件网点编码';
注意:字段类型、长度、默认值尽量与源库一致。
方案三:【长期治理】建立 Schema 变更流程
为了避免再次发生此类问题,建议建立标准化流程:
业务方提需求:需在 MySQL 加字段;
DBA 评估影响:确认是否需要同步到下游;
先改 StarRocks 表:手动执行 ALTER TABLE;
再上线源库变更:确保 CDC 任务能正常消费;
监控告警:对 CDC 任务增加 DDL 事件告警(可选)。
原则:"先下游,后上游"
四、如何优雅处理 DDL 事件?
如果你想要保留 DDL 通知能力(用于审计或告警),可使用 SchemaChangeMode.ALLOW,并在自定义反序列化器中处理:
sql
// 示例:捕获 DDL 并记录日志
public class SafeJsonDebeziumDeserializationSchema
extends JsonDebeziumDeserializationSchema {
@Override
public void deserialize(SourceRecord record, Collector<String> out) {
if (isDdlEvent(record)) {
String ddl = extractDdl(record);
log.warn("检测到 DDL 变更,请人工处理: {}", ddl);
// 不抛出异常,也不写入下游
return;
}
super.deserialize(record, out);
}
}
