上百张MySQL表要同步到数仓——DataX、Canal、Flink CDC怎么选?
本文目录
先说结论
一句话决策:离线T+1批量同步选DataX/Sqoop/SeaTunnel;准实时(秒级延迟)选Flink CDC;传统Java栈+Kafka中间层选Canal。
没有银弹:大公司往往三种并存——核心业务表Flink CDC、长尾维度表DataX、历史归档Canal到Kafka。
决策维度:延迟要求、表数量、Schema变更频率、对源库压力容忍度、团队技术栈。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
"我们要把MySQL同步到数仓"——这句话听起来简单,但背后藏着大量决策:要不要实时?全量怎么做?增量怎么做?Schema变了怎么办?做埋点治理那个项目时,光是同步链路就讨论了两周。这道题面试官想看的是你有没有架构思维,能不能根据业务诉求选合适的方案,而不是"我们公司用XX所以XX好"。
业务场景:上百张表的混合同步
印象很深的一个项目,MySQL实例有大概200张表要同步到Databricks。其中订单/支付20张表要求秒级(运营大盘用),用户/设备30张表要求T+1(数仓DWD层用),其余150张配置/日志表T+1甚至T+7就行。最后的方案是分层选型:
核心20张表 → Flink CDC → Kafka → Spark Structured Streaming → Delta Lake
重要30张表 → DataX 全量+小时级增量 → Delta Lake
长尾150张表 → DataX 每日全量 → Hive归档
理由:
- 核心表数据量大、字段多,Flink CDC自动捕获Schema变更
- 重要表对延迟不敏感,DataX简单稳定
- 长尾表全量同步比维护增量逻辑更省事,反正数据量小
这个分层选择背后的逻辑:用复杂方案换业务价值,简单场景用简单方案,不为"统一技术栈"硬上Flink CDC。
方案一:DataX/Sqoop——批量同步的工业标准
DataX的本质就是一个"读插件+写插件"的多线程数据搬运工。一个job跑一张表,读端通过JDBC分片并发拉取,写端攒批写入目标。优势是配置简单、稳定可控、对源库压力可预估。
{
"job": {
"setting": { "speed": { "channel": 8 } }, // 8个并发线程
"content": [{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "sync_user",
"connection": [{
"jdbcUrl": ["jdbc:mysql://slave-readonly:3306/order_db"],
"querySql": ["SELECT * FROM orders WHERE create_time >= '${bizdate}' AND create_time < '${nextdate}'"]
}]
}
},
"writer": {
"name": "hdfswriter",
"parameter": { "path": "/dwd/orders/dt=${bizdate}", "fileType": "parquet" }
}
}]
}
}
适用场景:T+1或小时级增量;表结构稳定;可以接受按时间字段切片;源库有从库可读。
不适用:表没有合适的增量字段(比如没有update_time);要求秒级延迟;Schema频繁变更。
方案二:Canal——传统CDC的代表
Canal模拟MySQL Slave拉Binlog,把Binlog事件转换成消息发到Kafka。优势是对源库无侵入(不查源库只拉Binlog),延迟在秒级,社区成熟。劣势是只做了"采集到Kafka"这一步,全量初始化、Schema演进、下游消费这些都要自己拼。
# canal instance.properties 关键配置
canal.instance.master.address = mysql-master:3306
canal.instance.dbUsername = canal
canal.instance.dbPassword = ****
canal.instance.connectionCharset = UTF-8
canal.instance.filter.regex = order_db\\..* # 只采order_db库
# 输出到Kafka
canal.mq.topic = canal_order_db
canal.mq.dynamicTopic = order_db\\..* # 按表名拆topic
canal.mq.partition = 0
canal.mq.partitionHash = order_db\\..*:$pk$ # 按主键hash
适用场景:已有Kafka中间层;下游有多个消费方(数仓+ES+缓存);团队Java栈成熟。
方案三:Flink CDC——一体化新势力
Flink CDC基于Debezium封装,把全量snapshot+增量binlog合一,整个链路就是一个Flink作业。最大优势是"全量增量自动衔接"——传统方案要先DataX跑全量、再用Canal接增量、还要解决衔接窗口的数据不一致;Flink CDC自带这个能力。
-- Flink SQL一行接入CDC
CREATE TABLE orders_cdc (
id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2),
create_time TIMESTAMP(3),
PRIMARY KEY (id) NOT ENFORCED
) WITH (
'connector' = 'mysql-cdc',
'hostname' = 'mysql-master',
'port' = '3306',
'username' = 'flink_cdc',
'password' = '****',
'database-name' = 'order_db',
'table-name' = 'orders',
'scan.incremental.snapshot.enabled' = 'true', -- 2.0+无锁snapshot
'scan.incremental.snapshot.chunk.size' = '8096'
);
-- 写入Hologres/Iceberg
INSERT INTO ods_orders SELECT * FROM orders_cdc;
适用场景:已有Flink集群;要求秒级延迟;多张表统一管理;Schema频繁变更。
不适用:没有Flink基础设施;只是T+1批量同步(杀鸡用牛刀);表特别大(单表100亿+)做snapshot压力大。
三方案对比矩阵
| 维度 | DataX/Sqoop | Canal | Flink CDC |
|---|---|---|---|
| 延迟 | T+1或小时级 | 秒级 | 秒级 |
| 全量+增量 | 需脚本拼接 | 需配合外部全量 | 原生一体化 |
| 源库压力 | 查源表(可走从库) | 拉Binlog无压力 | snapshot查表+Binlog |
| Schema演进 | 脚本维护 | 下游自己解析 | 支持(2.4+) |
| 运维门槛 | 低(单作业) | 中(HA+监控) | 中(Flink集群) |
| 状态/Exactly-Once | 无状态重跑 | 依赖下游幂等 | Checkpoint保证 |
| 典型场景 | 数仓ODS批量入仓 | 传统Kafka中转 | 实时数仓/湖仓 |
面试高频追问
追问一:为什么不全用Flink CDC统一掉?
三个原因:① 资源成本——一个MySQL CDC作业起码1个TM 2G内存,200张表全上需要大量资源;② 长尾表没必要——很多配置表一天就改几次,T+1全量更简单可控;③ 技术债——Flink CDC对DBA是黑盒,运维不便。原则是"业务价值匹配技术复杂度"。
追问二:Flink CDC 1.x和2.x最大区别是什么?
最关键是2.x引入了无锁snapshot算法。1.x在做全量初始化时要给整个表加全局读锁(FLUSH TABLES WITH READ LOCK),生产库根本不敢这么搞。2.x借鉴Netflix DBLog的Chunk-based算法,把表按主键切chunk并发读,每个chunk只用binlog对齐位点,不需要全局锁,对业务零影响。这是2.x能在生产大规模落地的核心。
追问三:DataX怎么解决数据漂移问题?
数据漂移是说"凌晨0点的批,跑出来的数据可能包含0点之后到数的T+1数据"。常见做法是:① 取数条件用源表的modify_time而不是create_time;② 多取15分钟做去重(取T日数据时WHERE modify_time BETWEEN '00:00:00' AND 'T+1 00:15:00');③ ODS层按modify_time分区,DWD层做ROW_NUMBER去重保留最新版本。
实战踩坑:DataX全量同步压垮主库
早期接手一个老的DataX作业,跑在主库上,channel配了32个并发,凌晨3点把主库QPS打到平时的3倍,DBA直接告警。后来定了三条规则:① 所有同步必须走从库(专门的readonly slave);② channel数根据表大小动态算(不是固定32);③ 加上querySql的where切片,不用splitPk全表扫描。
// 切片配置:用主键范围而不是channel数硬切
{
"reader": {
"parameter": {
"connection": [{
"jdbcUrl": ["jdbc:mysql://slave-readonly:3306/db?useCursorFetch=true"],
"querySql": [
"SELECT /*+ MAX_EXECUTION_TIME(600000) */ * FROM orders WHERE id BETWEEN 0 AND 10000000",
"SELECT /*+ MAX_EXECUTION_TIME(600000) */ * FROM orders WHERE id BETWEEN 10000001 AND 20000000"
] // 手动切片,每片1000万行,避免splitPk的不均匀
}]
}
},
"setting": {
"speed": { "channel": 4, "byte": 10485760 } // 限速10MB/s保护源库
}
}
解决方案:加了限速、走从库、SQL Hint加超时之后再没出过故障。这个事后专门写了《DataX上线规范》要求所有作业评审。
总结
CDC选型没有标准答案,关键是匹配业务诉求和团队能力。简单场景选DataX,传统Kafka栈选Canal,新建实时数仓直接Flink CDC。面试时千万别说"Flink CDC最好"——大厂真实现状是混合并存。说出"分层选型+成本权衡"的思路,比死记功能列表加分多。
下一篇聊更难的实战题:一张10亿行的订单表怎么平滑迁移到数仓?全量+增量怎么对齐?
你们公司CDC方案用的是哪一种?为什么这么选?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长