TL;DR(30 秒扫完)
- 5 亿订单的真相:5 亿 ÷ 100w/天 = 15 个月数据;留 3 个月热 = 1 亿;1 亿 ÷ 2000w = 5-6 表;规划 16 张(2 的幂)
- 冷热分离:热数据(3-6 个月)进 MySQL 分片;温数据(1-3 年)进 ES/ClickHouse;冷数据(>3 年)归档 OSS
- 迁移五阶段:准备 → 全量迁移 → 双写 → 灰度切流 → 下线
- 一致性三板斧:T-1 全表对账 + 抽样实时对账 + 反向 binlog
- 回滚原则:流量回滚,不是数据回滚;反向 binlog 保持老库始终有数据
关键结论
结论 A迁移成本 = 数据规模 × 阶段数,能降规模就降规模
结论 B反向 binlog 是回滚兜底——保持老库始终有数据,出问题 5 分钟切回
结论 C一开始就建 16 张表(2 的幂),留翻倍扩容空间
结论 D分片键选 user_id,主查询命中,异构索引表兜底订单号/状态查询
完整讲解(费曼四步)
STEP 1 · 概念
5 亿订单分库分表是端到端工程:分片方案设计 + 历史数据迁移 + 不停机双写 + 灰度切流 + 数据一致性验证 + 回滚兜底。STEP 2 · 大白话
搬家公司类比:- 5 亿件家具(订单)要从老房子(单库)搬到新大别墅(分片)
- 老房子太大一次搬不完 → 冷热分离:常用的搬进新屋,不常用的存仓库
- 搬家过程中老房子还要住人 → 双写:新动作两边都做
- 慢慢把日常活动(读流量)挪到新屋 → 灰度切流
- 万一搬错了 → 反向 binlog:新屋变更同步回老屋,随时能切回
STEP 3 · 底层
第一步:数据体量估算
假设日均 100w 单:- 5 亿 = 500 天 = 15 个月
- 保留 3 个月热数据 = 1 亿行
- 单表 2000w 上限 → 1 亿 ÷ 2000w = 5-6 张表
- 规划 16 张表(2 的幂,留翻倍扩容空间)
- 3-5 年后业务翻倍要扩容
- 8 → 16 只迁 50% 数据
- 6 → 8 要迁 75%,代价大得多
- 扩容代价远高于预留成本
第二步:分片键选择
为什么选 user_id?| 方案 | 主查询命中 | 数据均匀 | 不可变 | 分片键 |
|---|---|---|---|---|
| user_id | ✅ 我的订单(90%) | ✅ 雪花 ID | ✅ | 推荐 |
| create_time | ❌ 用户订单跨时段 | ✅ 时序分布 | ✅ | 时序归档 |
| order_id | ❌ 用户订单跨片 | ✅ 雪花 ID | ✅ | 需异构兜底 |
-- 异构索引表:按 order_id 分片
order_index(order_id, user_id, status)
-- 异构索引表:按 status 分片
status_index(status, order_id, user_id)
订单号查询、状态筛选走异构表。
第三步:冷热分层存储
| 层 | 时间范围 | 存储 | 用途 |
|---|---|---|---|
| 热 | 3-6 个月 | MySQL 分片 | 主业务查询 |
| 温 | 1-3 年 | ES / ClickHouse | 搜索、报表 |
| 冷 | >3 年 | OSS 归档 | 审计、按需恢复 |
第四步:不停机迁移五阶段
阶段 1 · 准备(1 周)
├─ 新分片建表(16 张)
├─ Canal / DTS 部署
├─ 异构索引表建好
└─ 应用双写代码 ready
阶段 2 · 全量迁移(1 周)
├─ 1 亿热数据分批 ETL 到新分片
├─ 工具:Canal 全量抓 / gh-ost / DTS
├─ 100w/分钟 ≈ 80 小时(多机并行可压到 24h)
└─ 每日 T-1 全表对账(行数 + checksum)
阶段 3 · 双写(1-2 周)
├─ 应用层双写主库 + 新分片
├─ Canal 反向监听新分片兜底老库
├─ 本地事务保证主表 + 索引表一致性
└─ 实时对账:抽样 1% 订单异步校验
阶段 4 · 灰度切流(1 周)
├─ 读流量 1% → 10% → 50% → 100%
├─ 每档观察 24 小时业务指标
│ - 订单成功率
│ - P99 延迟
│ - 告警数
│ - 分片水位均衡度
└─ 出问题立即切回
阶段 5 · 切换 + 下线(1-2 周)
├─ 100% 读切后停老库写入
├─ 老库只读保留 7 天观察期
├─ 无异常下线
└─ 反向 binlog 关闭
第五步:一致性保证
三大工具:- T-1 全表对账:每日跑一次全表行数 + checksum + 抽样 1w 条明细
- 实时对账:写后抽样 1% 订单异步校验(延迟 < 5 秒)
- 反向 binlog:Canal 监听新分片 → 老库,保持老库始终有数据
| 一致性级别 | 场景 | 方案 |
|---|---|---|
| 强一致 | 主表 + 异构索引表 | 本地事务 |
| 最终一致 | 主库 → 新分片 | Canal 异步 |
| 弱一致 | 主库 → ES | CDC + 延迟容忍 |
第六步:回滚策略
核心原则:流量回滚,不是数据回滚。正常路径: 老库写入 → Canal → 新分片同步
↓
灰度切流: 新分片承载读流量
出问题:
├─ 立即把读流量切回老库(< 5 分钟)
├─ 立即把写流量切回老库
├─ 反向 Canal 保持老库有数据(自动)
└─ 新分片问题修好再重新切流
为什么不做数据回滚?- 数据回滚成本高、风险大
- 反向 binlog 已经保证老库有数据
- 新分片问题通常是代码 bug 或数据脏,不是数据缺失
第七步:分片规模预留决策树
业务预估 3-5 年后目标规模
↓
├─ 单表 × N ≤ 1000w 行?
│ ├─ 是 → 选 N = 2 的幂
│ └─ 否 → 需要冷分离
↓
预估目标分片数 = ceil(目标规模 / 2000w)
↓
向上取到 2 的幂
↓
建表(即使当前用不到也建好,避免以后扩容)
STEP 4 · 简化
一句话总结:先估算降规模 → 选 user_id 分片 → 建 16 张表 → 五阶段迁移 → 反向 binlog 兜底。核心认知是"迁移成本 = 规模 × 阶段数"。 记忆口诀:- 分片键:主查询命中优先
- 规模:留 2 的幂,避免以后扩容
- 冷热:热分片,温 ES,冷 OSS
- 迁移:准备 → 全量 → 双写 → 灰度 → 下线
- 一致性:T-1 对账 + 实时抽样 + 反向 binlog
- 回滚:流量回滚,不是数据回滚
常见误区
说"5 亿数据全部分片"
冷热分离降规模到 1/5,热数据才进分片
说"用 create_time 做分片键"
用户订单跨所有时间段,一次查询打所有分片,完全违背分片目的
说"回滚就是数据迁回"
回滚是流量切回,反向 binlog 已经保持老库有数据
说"迁移期间业务停摆"
不停机迁移是标准做法,Canal 双写 + 灰度切流实现
说"分片规模够用就行"
留 2 的幂是扩容成本控制,一次性到位比以后扩容便宜得多
说"双写用异步 MQ 就行"
主表 + 异构索引表必须本地事务保证一致;新分片同步可以异步 Canal
延伸追问
双写期间如何保证主库和新分片数据一致?
本地事务保证主表 + 异构索引表;Canal 异步同步到新分片;每日 T-1 全表对账;抽样实时对账;反向 Canal 兜底
如果灰度切流后发现问题,回滚怎么做?
立即把流量切回老库(< 5 分钟);反向 binlog 保证老库始终有数据;数据不迁回,等新分片修好再重新切流
5 亿行数据全量迁移要多久?如何加速?
Canal 全量抓 100w/分钟 → 5000 分钟 ≈ 80 小时;多机并行 + 多线程 INSERT 可压到 24 小时
分片规模选择:现在 5 张表够,为什么建议一开始就建 16 张?
留 2 的幂扩容余量;后期扩容代价远高于预留成本;数据迁移是最高危操作
分片键选 user_id 的理由?为什么不选 create_time 或 order_id?
主查询"我的订单"命中 user_id(90% 流量);create_time 分片会导致用户订单跨所有分片;order_id 分片导致用户查询跨所有分片
Canal 和 DTS 有什么差别?
Canal 是阿里开源的 binlog 订阅工具;DTS 是云厂商的服务,功能更全但收费。自研场景用 Canal,云上业务用 DTS
反向 binlog 有性能风险吗?
反向 binlog 是异步的,性能几乎无影响;主要风险是延迟(通常秒级),需要在业务侧接受这个延迟
速查表
数据估算: 5 亿 = 15 个月 → 留 3 个月 = 1 亿 → 建 16 张表(2 的幂)
分片键: user_id(主查询命中)+ 异构索引表兜底
冷热分层: 热 3-6 月进 MySQL / 温 1-3 年进 ES / 冷 >3 年进 OSS
五阶段: 准备 → 全量迁移 → 双写 → 灰度切流 → 下线
一致性: 本地事务 + T-1 全表对账 + 抽样实时对账 + 反向 binlog
灰度切流: 1% → 10% → 50% → 100%,每档观察 24h
回滚: 流量回滚(< 5 分钟),不是数据回滚
预留: 留 2 的幂,避免以后扩容
Anki 候选卡片
Q: 5 亿订单分库分表的体量估算?
A: 5 亿 ÷ 100w/天 = 15 个月;留 3 个月热 = 1 亿;1 亿 ÷ 2000w = 5-6 表;规划 16 张(2 的幂)
Q: 为什么选 user_id 做分片键?
A: 主查询"我的订单"(90%)命中 user_id;create_time 分片会导致用户查询跨所有分片
Q: 5 亿订单冷热分层存储?
A: 热 3-6 月进 MySQL 分片 / 温 1-3 年进 ES/ClickHouse / 冷 >3 年进 OSS
Q: 不停机迁移五阶段?
A: 准备 → 全量迁移 → 双写 → 灰度切流 → 下线
Q: 迁移期间一致性三大工具?
A: T-1 全表对账(行数 + checksum + 抽样)+ 抽样实时对账 + 反向 binlog
Q: 回滚的核心原则?
A: 流量回滚,不是数据回滚;反向 binlog 保持老库始终有数据,5 分钟内切回
Q: 为什么一开始建 16 张表?
A: 留 2 的幂扩容余量;8→16 只迁 50% 数据;6→8 要迁 75%;扩容代价远高于预留成本
关联题目
- ✅ 《5亿订单分库分表与双写灰度迁移》— 2026-09-23 Round 1, ⭐⭐⭐⭐⭐(最出彩一题,从 09-15 首次 70/100 显著进步到 5/5)
- ⚠️ 《5亿订单分库分表与双写灰度迁移》— 2026-09-15 Round 1, 70/100(首次答错:分片键选时间维度是错的)
关联知识
迁移成本 = 数据规模 × 阶段数,能降规模就降规模,反向 binlog 兜底
搬家公司:老房子=单库,新别墅=分片。搬家期间老房子还要住人 → 双写+灰度切流;万一搬错了 → 反向 binlog 保持老屋有数据,随时切回
✦ 记 忆 口 诀 ✦
估算降规模 / user_id 分片 / 建 16 张表 / 五阶段 / 反向 binlog 流量回滚
关键可视化
5 亿订单体量估算与降规模
flowchart TB A[5 亿订单 15 个月数据] --> B[冷 热分离] B --> H[热数据 近 3 个月 1 亿行] B --> W[温数据 1 到 3 年 ES 或 ClickHouse] B --> C[冷数据 大于 3 年 OSS 归档] H --> D[分片真目标 1 亿行] D --> E[单表 2000w 上限] E --> F[需要 5 到 6 张表] F --> G[向上取 2 的幂 16 张] G --> G2[留翻倍扩容空间]
分片键决策树
flowchart TB A[订单分片键选择] --> B[主查询命中优先] B --> C[我的订单 90 主查询] C --> D[user_id 分片 推荐] B --> E[反例 create_time] E --> F[用户订单跨所有分片] B --> G[反例 order_id] G --> H[用户查询跨所有分片] D --> I[订单号 状态查询 走异构索引表兜底]
不停机迁移五阶段
flowchart LR S1[阶段 1 准备 1 周] --> S2[阶段 2 全量迁移 1 周] S2 --> S3[阶段 3 双写 1 到 2 周] S3 --> S4[阶段 4 灰度切流 1 周] S4 --> S5[阶段 5 切换 加 下线 1 到 2 周] S1 --> A1[新分片建表 16 张] S1 --> A2[Canal 加 DTS 部署] S1 --> A3[异构索引表建好] S2 --> A4[1 亿热数据 ETL] S2 --> A5[每日 T 减 1 全表对账] S3 --> A6[应用层双写 主库 加 新分片] S3 --> A7[Canal 反向监听兜底老库] S4 --> A8[读流量 1 到 10 到 50 到 100 百分号] S4 --> A9[每档观察 24 小时] S5 --> A10[老库只读 7 天观察期] S5 --> A11[无异常下线 反向 binlog 关闭]
迁移期一致性三大工具
flowchart TB C[一致性保证] --> A[本地事务] A --> A1[主表 加 异构索引表] A --> A2[写路径强一致] C --> B[T 减 1 全表对账] B --> B1[每日跑一次] B --> B2[行数 加 checksum 加 抽样 1w] C --> C1[抽样实时对账] C1 --> C2[写后抽样 1 百分号] C1 --> C3[异步校验 延迟小于 5 秒] C --> D[反向 binlog] D --> D1[Canal 监听新分片 到 老库] D --> D2[保持老库始终有数据] D --> D3[回滚兜底]
回滚流程:流量回滚 而非 数据回滚
flowchart TB N[正常路径] --> P1[老库写入] P1 --> P2[Canal 同步新分片] P2 --> P3[反向 Canal 老库兜底] P3 --> P4[灰度切流 新分片承载读] P4 --> B[出问题] B --> R1[立即切读流量回老库 5 分钟内] B --> R2[立即切写流量回老库] B --> R3[反向 binlog 保持老库有数据 自动] B --> R4[新分片问题修好再重新切流] R4 --> X[不做数据回滚] X --> X1[成本 高风险大] X --> X2[反向 binlog 已保证老库有数据] X --> X3[新分片问题通常代码 bug 而非数据缺失]
分片规模预留决策树
flowchart TB
A[业务预估 3 到 5 年后目标规模] --> B[目标规模 除以 2000w 得到分片数]
B --> C{是 2 的幂?}
C -->|是| D[直接用]
C -->|否| E[向上取 2 的幂]
E --> F[建表 即使当前用不到]
F --> G[避免以后扩容]
G --> H[8 到 16 只迁 50 百分号 数据]
G --> I[6 到 8 要迁 75 百分号 数据]知识关系
🔄 延伸(Extends)
暂无
🎯 概念
📏 规则
⚠️ 误区
🔍 追问
✨ 口诀
共 0 张卡,点击翻面