设计一个实时数据同步系统,将 MySQL 数据实时同步到数据仓库?
- 标签:后端、场景题、系统设计题
🏛 业务架构师
MySQL 实时同步数仓,我上一题讲了订单场景的完整链路,这题聚焦实时化本身:技术选型、关键机制、以及实时 vs 批量的业务决策。
技术选型(当前主流)
- Flink CDC(我首选):直接读 binlog,无需部署中间件,exactly-once 语义内建,支持全量 + 增量自动衔接(首次同步全量快照,随后无缝切 binlog 位点)
- Canal + Kafka:Canal 解析 binlog 发 MQ,消费端自研逻辑——灵活性高,组件多一步运维
- 云厂商 DTS:托管省心,适合不想养 Flink 集群的团队
实时同步的关键机制
- 全量 + 增量一体化:新表接入时的存量数据如何搬——Flink CDC 的 snapshot 阶段 + 位点续传解决"从零开始"
- schema 演进:源表加字段,链路要能自动或半自动跟上(否则一条变更消息导致消费失败、全链路堆积)——这是 CDC 运维的头号日常问题
- 断点续传:Flink checkpoint 保存 binlog 位点,故障重启从位点继续,不丢不重
- 乱序处理:按主键分区保序;多表间的关联时序(订单表和流水表)用状态缓存 + 事件时间对齐
业务决策(架构师差异点)
实时同步的成本不低(Flink 集群 7×24 运行、人力运维),值不值得要问业务:
- 必须实时:大促实时 GMV 大盘、实时风控(异常订单拦截)、商家实时经营看板
- T+1 足够:财务结算、月报、商家对账——这类需求上实时链路是纯浪费
我的实践:核心交易表实时 CDC,长尾表 T+1 批量,两套并存按需升级。另外提醒:实时链路的数据质量要靠对账兜底(小时级增量对账),"实时"和"准确"没有自动绑定,实时地同步错误数据是更大的灾难。
🏗 系统架构师
MySQL 到数仓的实时同步,我按采集、传输、加工、落库、一致性五段讲完整方案。
1. 采集层:binlog CDC
- 工具选型:Canal(阿里系、成熟)、Debezium(云原生/Kafka Connect 生态)、Flink CDC(CDC Source 直连,省去 Kafka 也可)
- 前提:MySQL 开 ROW 格式 binlog,
GTID开启(位点管理与幂等重放的基础) - 一致性起点:先记录 binlog 位点(
SHOW MASTER STATUS)或用 GTID 快照,再拉全量,随后从位点回放增量——保证全量与增量无缝衔接不丢不重(Flink CDC 的无锁快照正是做这件事)
2. 传输层:Kafka
- Topic 按表分,分区键用主键(订单 ID)——同主键变更进同分区,天然有序
- 保留周期覆盖故障恢复窗口(如 3 天);schema 演进用 Schema Registry 管理
3. 加工层:Flink
- 反序列化 binlog JSON/Protobuf → 统一 ChangeEvent(insert/update/delete)
- 维表关联补齐(宽表加工)、打宽后按小时分区键写数仓
- 精确一次(exactly-once):checkpoint 开启,两阶段提交 sink
4. 落库层:湖仓选型
- 实时分析场景:Doris/StarRocks(Unique Key 模型主键去重,upsert 语义)
- 数据湖:Hudi(MOR + 主键去重、分钟级可见)/ Iceberg(配合 upsert 能力)
- 消费语义:CDC 到湖仓的标准模型是upsert 流,靠主键模型消化 update/delete
5. 一致性与运维:
- 端到端延迟监控:源 commit 时间与数仓可见时间差,P99 < 1 分钟
- 分区完整性:小时分区在 watermark 后才对下游可见,避免读到半截数据
- schema 变更同步:源加字段→ CDC 透传 → 数仓表自动加列(Doris/Hudi 支持 schema evolution)
- 故障恢复:Flink checkpoint + Kafka 位点,重启续传;对账任务按日校验 count/sum
方案对比一句话:简单需求 Flink CDC 直连(组件少);多消费者/大流量加 Kafka 缓冲(解耦与回放能力)。
💻 全栈开发专家
实时同步到数仓,我直接给生产验证过的架构:MySQL binlog → Canal/Debezium → Kafka → 流式消费 → Doris/StarRocks 主键表。
数据链路
text
MySQL (binlog_format=ROW, GTID 开启)
→ Canal Server (伪装 slave dump 协议)
→ Kafka (按表分 topic, 订单 ID 做 key)
→ 消费端 (批量聚合, Stream Load 写入)
→ 数仓 Unique Key 模型表 (按天分区)全量 + 增量的衔接(必考细节)
冷启动不能只靠 binlog(历史数据不在里面):先做一致性快照——短暂加全局读锁(或 FTWRL)记录 binlog 位点,导出全量,释放锁,再从记录的位点接增量。Debezium 的 snapshot.mode=initial 把这套流程自动化了。全量期间锁窗口通常秒级,要选低峰执行。
关键工程点
- 断点续传:binlog 位点(gtid + offset)持久化,进程重启、Kafka 重放都不丢不重。
- 幂等:数仓侧主键模型 upsert,同 key 后写覆盖先写;再带上 binlog 时间戳做版本比较,防乱序回放旧数据覆盖新数据。
- DDL 变更:加列直接透传;消费端 schema 演进要兼容(新增字段可空),破坏性变更走"新表迁移 + 切流"。
- 延迟监控:对比 Canal 当前订阅位点与 MySQL 实时位点,差值超阈值(如 30s)告警,这是系统健康度的核心指标。
坑
- binlog 保留期过短,同步程序挂久了重启会丢位点和数据,MySQL 侧
expire_logs_days至少留 7 天。 - 大事务(一条 binlog event 几十万行)会打爆消费端,要按行拆批并限制单批字节。