TL;DR(30 秒扫完)
- 分表 vs 分库:分表分散数据文件(解决 B+ 树深/单文件大),分库分散实例(解决连接池/QPS/故障域)
- 分区 vs 分表:分区是引擎层(同一实例内文件切分,业务透明),分表是逻辑层(跨实例水平扩)
- 分片键五大原则:①主查询命中 ②分布均匀 ③不可变 ④避免 NULL ⑤低基数陷阱
- 2 的幂扩容:
x % (2N)只影响x % N ≥ N的一半数据 → 翻倍扩容恒定迁 50% - 数据倾斜两类:数据量倾斜(存储/IO)+ 访问倾斜(QPS/热点 key),后者更常见更难
关键结论
结论 A分库分表是最后手段,前面还有读写分离、垂直拆分、缓存前置等更便宜的方案
结论 B分片键一旦选定,改就是数据大迁移,选错比不做还痛
结论 C分区能扩文件扩不了实例,分表可以一路扩到跨机房
结论 D2 的幂是扩容可预测性的数学基石,不是"计算机喜欢 2"
完整讲解(费曼四步)
STEP 1 · 概念
分库分表是数据库水平扩展方案:把单库/单表的数据按分片键水平切分成多份,放到多个库/表上,通过中间件或应用层做路由。STEP 2 · 大白话
图书馆类比:- 一个图书馆(单库)放不下所有书(数据太多)→ 分库 = 开多个图书馆
- 一个书架(单表)挂不下所有书 → 分表 = 加书架
- 但书架的分区(分区)只是给同一图书馆内部分层,图书馆数量没变
STEP 3 · 底层
分表 vs 分库 vs 分区(一张表看穿)
| 维度 | 分表 | 分库 | 分区 |
|---|---|---|---|
| 实现层次 | 应用/中间件层 | 应用/中间件层 | 引擎层(InnoDB) |
| 表名 | 多张物理表 | 多张物理表 | 单一逻辑表名 |
| SQL 改造 | 需要路由 | 需要路由 | 完全透明 |
| 扩容边界 | 跨实例 | 跨实例 | 同一实例 |
| 连接池 | 分散 | 分散 | 不分散 |
| 故障域 | 隔离 | 隔离 | 不隔离 |
| 典型场景 | 数据量巨大 | 高并发 | 归档、时序、数据生命周期 |
单库单表的瓶颈来源
分表瓶颈(存储层):- B+ 树层高随行数增长,磁盘 IO 线性增加
- 二级索引与聚簇索引数据文件过大
- DDL 变更阻塞时间线性增长
- 扫描型 SQL 无法覆盖索引
- 连接池上限(
max_connections) - 单实例 QPS/TPS 上限
- CPU、内存上限
- 单点故障影响全部业务
- 单表 500w-2000w 行
- B+ 树高度 ≥4 层
- 单库连接数 / QPS 打满
分片键五大原则
| # | 原则 | 反面案例 |
|---|---|---|
| 1 | 主查询路径命中 80%+ | 订单按 status 分片但主查询是"我的订单" |
| 2 | 数据分布均匀 | 按 status 分片,90% 订单是"已支付" |
| 3 | 字段不可变 | 用 city、status 这种会变动的字段 |
| 4 | 避免 NULL | NULL 无法哈希会集中到默认分片 |
| 5 | 低基数陷阱 | 用 order_type 只有 3 个值 |
数据倾斜的两类
数据量倾斜:存储和 IO 集中在少数分片,热点分片磁盘 90% 满,其他 10%。 访问倾斜(QPS 倾斜):热点 key / 大卖家 / 明星用户把所有请求打到同一分片。 常见成因:- 分片键选择不当(用创建时间,但 80% 流量在近期)
- 分片键值域不均(大客户 ID 号段集中)
- 哈希冲突(哈希空间过小)
- 扩容后未重平衡
- 事前预防:分片键选均匀、可查、不可变
- 事中监控:Prometheus 抓各分片行数、磁盘水位、QPS、TPS、连接数;方差和 P99/均值比 > 3 告警
- 事后治理:二次分片、复合分片、一致性哈希虚拟节点、大卖家独立路由、双写迁移
2 的幂扩容的数学本质
取模扩容迁移比例:- 任意 N → N+1:几乎所有数据都要重新分配(旧
hash(x) % N,新hash(x) % (N+1),两者几乎无关) - 2N → 4N(翻倍扩容):迁移恒定 50%
旧分片号 s_old = hash(x) % (2N)
新分片号 s_new = hash(x) % (4N)
因为 hash(x) = s_old + k * 2N,取模 4N 后:
- k 偶数 → s_new = s_old(不动)
- k 奇数 → s_new = s_old + 2N(迁一次)
所以每条数据最多迁一次,一半不动。
对比反例:N=4 → N=5:迁移约 80%N=8 → N=9:迁移约 88.9%N=8 → N=16:迁移恒为 50%
// N 是 2 的幂时
hash(x) & (N - 1) == hash(x) % N // 等价
// 位与运算 1 周期,模运算几十周期
分片规模估算(3 步走)
- 数据体量估算:当前总量 ÷ 日均增速 = 数据年龄(月)
- 冷热分离:热数据保留周期(通常 3-6 个月)→ 真需要分片的量
- 表数选择:热数据量 ÷ 单表上限(2000w)= 最小表数,向上取到 2 的幂
STEP 4 · 简化
一句话总结:分库分表 = 分片键选对 + 规模选 2 的幂 + 冷热分离降规模 + 提前对齐排序字段。 记忆口诀:- 分表 vs 分库:表散文件 / 库散实例
- 分区 vs 分表:引擎内 / 逻辑层
- 分片键五原则:命中、均匀、不变、非空、高基数
- 2 的幂:翻倍恒定迁 50%
常见误区
说"二进制所以选 2 的幂"
取模扩容的数学性质(
x % (2N) 只影响一半数据)把"分区"和"分表"当成一回事
分区只能扩文件、扩不了实例;分表可以跨机扩
把"数据倾斜"只当成"分片键选错"
还有扩容未重平衡、哈希冲突、热点 key等来源
说"分库分表是万能的"
跨分片 JOIN、分布式事务、全局 ID、二次扩容都是代价,是最后手段
忽略分区对主键的强约束
MySQL 要求分区键必须包含在所有主键/唯一键里,否则行归属不明确
延伸追问
单库瓶颈和单表瓶颈有什么差别?
单库瓶颈是实例级(连接池/QPS/故障域),单表瓶颈是存储级(B+ 树深/文件大/DDL 阻塞)。解决手段不同:分库 vs 分表。
分区表为什么必须把分区键放进主键?
MySQL 要求唯一键必须包含分区键,否则无法确定行归属到哪个分区。所以按时间分区但主键是自增 ID 时,得把分区键并进主键。
DROP PARTITION 为什么比 DELETE 快几个数量级?DROP PARTITION 直接 unlink 子文件,不做行级删除、不写 undo、不回收碎片。DELETE 要逐行处理 + purge 队列。
一致性哈希为什么能只迁 1/N?
环形映射下扩容只在相邻节点之间迁移,物理节点从 N 变 N+1,只影响原本落在最后一段虚拟点的数据(约 1/N)。
分片键选错线上怎么救?
双写新旧分片 → 全量回补 → 灰度切读 → 校验对账 → 反向迁移 → 下线旧分片。依赖 ShardingSphere 这类中间件的在线 DDL 能力。
什么情况下不需要分库分表?
单机未瓶颈 + 有归档/裁剪需求 → 只用分区;QPS 未到瓶颈但数据量大 → 只读副本 + 缓存;业务有明确读写分离 → 先做垂直拆分。
速查表
分表 vs 分库: 表散文件 / 库散实例
分区 vs 分表: 引擎内文件 / 逻辑层表
分片键五原则: 命中 / 均匀 / 不变 / 非空 / 高基数
2 的幂扩容: 翻倍恒定迁 50%(N→N+1 迁 80%+)
位掩码: hash(x) & (N-1) 仅 2 的幂等价 % N
触发阈值: 500w-2000w 行 / B+ 树 ≥4 层 / 连接池打满
倾斜两类: 数据量倾斜 / 访问倾斜(QPS)
治理三阶段: 事前选键 / 事中监控 / 事后治理
规模估算: 数据量÷增速=月数 → 冷分离 → 向上取 2 的幂
Anki 候选卡片
Q: 分表 vs 分库的核心差别?
A: 分表分散数据文件(存储层),分库分散实例(连接池/QPS/故障域)
Q: 分区和分表的实现层次差异?
A: 分区在引擎层(同一实例内文件切分,SQL 透明);分表在逻辑层(跨实例水平扩,需 SQL 路由)
Q: 分片键五大原则?
A: ①主查询命中 ②分布均匀 ③不可变 ④避免 NULL ⑤低基数陷阱
Q:
x % (2N) 与 x % N 的扩容关系?A: 只影响
x % N ≥ N 的一半数据;翻倍扩容恒迁 50%Q: 数据倾斜的两种类型?
A: 数据量倾斜(存储/IO 热点)+ 访问倾斜(QPS/热点 key)
Q: 触发分库分表的三个阈值?
A: 单表 500w-2000w 行、B+ 树高度 ≥4、单库连接数/QPS 打满
关联题目
- ✅ 《什么是分库?分表?分库分表?》— 2026-09-23 Round 1, ⭐⭐⭐⭐
- ⚠️ 《什么是数据倾斜,会带来哪些问题?如何解决?》— 2026-09-23 Round 1, ⭐⭐⭐(漏事前+事中)
- ✅ 《分区和分表有什么区别?》— 2026-09-23 Round 1, ⭐⭐⭐⭐
- ⚠️ 《分表字段如何选择?》— 2026-09-23 Round 1, ⭐⭐⭐⭐(缺不可变性原则)
- ⚠️ 《分库分表的数量为什么一般选择 2 的幂?》— 2026-09-23 Round 1, ⭐⭐⭐(漏数学推导)
关联知识
分库分表 = 分片键选对 + 规模选 2 的幂 + 冷热分离降规模
图书馆:分表=加书架,分库=开图书馆,分区=书架分层。分片键选错等于把书乱放,找书比不换更快
✦ 记 忆 口 诀 ✦
表散文件 / 库散实例 / 分区在引擎内 / 2 的幂翻倍恒定迁 50%
关键可视化
分表 vs 分库 vs 分区
flowchart TB R[数据水平扩展] --> A[分表 逻辑层] R --> B[分库 逻辑层] R --> C[分区 引擎层] A --> A1[多张物理表] A --> A2[需要 SQL 路由] A --> A3[同实例 或 跨实例] B --> B1[多个数据库实例] B --> B2[分散连接池 QPS 故障域] B --> B3[跨实例 跨机房] C --> C1[单一逻辑表名] C --> C2[SQL 完全透明] C --> C3[仅同实例内文件切分]
分片键五大原则
flowchart TB S[分片键选择] --> P1[主查询命中 80 以上] S --> P2[数据分布均匀] S --> P3[字段不可变] S --> P4[避免 NULL] S --> P5[低基数陷阱] P1 -.反例.-> X1[按 status 分片 但主查询是我的订单] P2 -.反例.-> X2[按 status 分片 90 已支付] P3 -.反例.-> X3[用 city 或 status 会变字段] P4 -.反例.-> X4[NULL 集中默认分片] P5 -.反例.-> X5[order_type 只有 3 个值]
2 的幂扩容数学本质
flowchart TB M[Mod 取模扩容] --> N1[N 扩到 N 加 1] N1 --> R1[迁移约 N 除以 N 加 1] R1 --> W1[例子 8 到 9 迁 88.9 百分号] M --> N2[2N 扩到 4N 翻倍] N2 --> R2[恒定迁移 50 百分号] R2 --> W2[数学本质 只有 x 模 N 大于等于 N 才迁] M --> N3[bitmask 位掩码优化] N3 --> R3[hash x and N 减 1 等价 hash x mod N] R3 --> W3[仅 2 的幂时成立 位运算 1 周期]
数据倾斜治理三阶段
flowchart LR S[数据倾斜] --> T1[数据量倾斜 存储 IO 热点] S --> T2[访问倾斜 QPS 热点 key] T1 --> G1[事前 分片键三原则] T1 --> G2[事中 Prometheus 监控 分片水位] T1 --> G3[事后 二次分片 一致性哈希 大卖家独立路由] T2 --> H1[事前 分片键分布均匀] T2 --> H2[事中 热点分片 QPS 均值比 大于 3 告警] T2 --> H3[事后 热点缓存 双写迁移 二次分片]
分片规模估算 3 步走
flowchart TB A[数据体量估算] --> B[冷 热分离] B --> C[表数选择] A --> A1[5 亿 除以 100w 每天 等于 15 个月] B --> B1[留 3 个月热数据 等于 1 亿] C --> C1[1 亿 除以 2000w 等于 5 到 6 张] C1 --> C2[向上取 2 的幂 16 张] C2 --> C3[留翻倍扩容空间]
知识关系
⬆️ 前置(Prerequisite)
暂无⚡ 对比(Contrast)
MySQL 事务核心— MySQL 是存储引擎层能力;分库分表是架构层方案,两者互补Redis 单线程— Redis 用 Cluster 分片实现水平扩,MySQL 用分库分表;两者都是水平扩但机制不同
🎯 概念
📏 规则
⚠️ 误区
🔍 追问
✨ 口诀
共 0 张卡,点击翻面