订阅更新

上百张MySQL表要同步到数仓——DataX、Canal、Flink CDC怎么选?

数据采集手册 第 2 题 约 10 分钟读完

本文目录
  1. 先说结论
  2. 业务场景:上百张表的混合同步
  3. 方案一:DataX/Sqoop——批量同步的工业标准
  4. 方案二:Canal——传统CDC的代表
  5. 方案三:Flink CDC——一体化新势力
  6. 三方案对比矩阵
  7. 面试高频追问
  8. 实战踩坑:DataX全量同步压垮主库
  9. 总结

先说结论

一句话决策:离线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/SqoopCanalFlink 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年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器