---
title: 分库分表基础概念 + 分片规模设计
type: concept
domain: sharding
tags: [sharding, database-sharding, partition, skew, design-principles]
status: learning
created: 2026-09-23
last_reviewed: 2026-09-23
method: feynman
related_questions:
  - "什么是分库？分表？分库分表？"
  - "分区和分表有什么区别？"
  - "什么是数据倾斜，会带来哪些问题？如何解决？"
  - "分库分表的数量为什么一般选择 2 的幂？"
  - "分表字段如何选择？"
related_knowledge:
  - ./sharding-algorithms-id.md
  - ./sharding-join-pagination.md
  - ./sharding-migration.md
  - ../mysql/mvcc.md
  - ../mysql/mysql-transaction-core.md
anki_cards: 6
interview_rounds:
  - "2026-09-23-round-1-Q1"
  - "2026-09-23-round-1-Q2"
  - "2026-09-23-round-1-Q3"
  - "2026-09-23-round-1-Q4"
  - "2026-09-23-round-1-Q6"
---

# 分库分表基础概念 + 分片规模设计

> 分库分表是解决单机瓶颈的最终方案，但选错分片键/规模会付出比不做更高的代价。核心是分清"分表 vs 分库 vs 分区"、理解"分片键五原则"、掌握"2 的幂扩容"的数学本质。

## TL;DR（30 秒扫完）

- **分表 vs 分库**：分表分散**数据文件**（解决 B+ 树深/单文件大），分库分散**实例**（解决连接池/QPS/故障域）
- **分区 vs 分表**：分区是**引擎层**（同一实例内文件切分，业务透明），分表是**逻辑层**（跨实例水平扩）
- **分片键五大原则**：①主查询命中 ②分布均匀 ③不可变 ④避免 NULL ⑤低基数陷阱
- **2 的幂扩容**：`x % (2N)` 只影响 `x % N ≥ N` 的一半数据 → 翻倍扩容恒定迁 50%
- **数据倾斜两类**：数据量倾斜（存储/IO）+ 访问倾斜（QPS/热点 key），后者更常见更难

## 关键结论

- **结论 A**：分库分表是**最后手段**，前面还有读写分离、垂直拆分、缓存前置等更便宜的方案
- **结论 B**：分片键一旦选定，改就是**数据大迁移**，选错比不做还痛
- **结论 C**：分区能扩文件扩不了实例，分表可以一路扩到跨机房
- **结论 D**：2 的幂是**扩容可预测性**的数学基石，不是"计算机喜欢 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 **打满**

命中 2 项以上就该做分片评估。

#### 分片键五大原则

| # | 原则 | 反面案例 |
|---|------|---------|
| 1 | 主查询路径命中 80%+ | 订单按 status 分片但主查询是"我的订单" |
| 2 | 数据分布均匀 | 按 status 分片，90% 订单是"已支付" |
| 3 | 字段不可变 | 用 city、status 这种会变动的字段 |
| 4 | 避免 NULL | NULL 无法哈希会集中到默认分片 |
| 5 | 低基数陷阱 | 用 order_type 只有 3 个值 |

#### 数据倾斜的两类

**数据量倾斜**：存储和 IO 集中在少数分片，热点分片磁盘 90% 满，其他 10%。

**访问倾斜（QPS 倾斜）**：热点 key / 大卖家 / 明星用户把所有请求打到同一分片。

**常见成因**：
- 分片键选择不当（用创建时间，但 80% 流量在近期）
- 分片键值域不均（大客户 ID 号段集中）
- 哈希冲突（哈希空间过小）
- 扩容后未重平衡

**治理三阶段**：
1. **事前预防**：分片键选均匀、可查、不可变
2. **事中监控**：Prometheus 抓各分片行数、磁盘水位、QPS、TPS、连接数；方差和 P99/均值比 > 3 告警
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%**

**位掩码优势**：
```java
// N 是 2 的幂时
hash(x) & (N - 1) == hash(x) % N   // 等价
// 位与运算 1 周期，模运算几十周期
```

#### 分片规模估算（3 步走）

1. **数据体量估算**：当前总量 ÷ 日均增速 = 数据年龄（月）
2. **冷热分离**：热数据保留周期（通常 3-6 个月）→ 真需要分片的量
3. **表数选择**：热数据量 ÷ 单表上限（2000w）= 最小表数，向上取到 2 的幂

**示例**：5 亿订单，日均 100w → 15 个月数据；留 3 个月热数据 = 1 亿；1 亿 ÷ 2000w = 5-6 表；规划 **16 张**（2 的幂，留翻倍余量）。

### STEP 4 · 简化

**一句话总结**：分库分表 = 分片键选对 + 规模选 2 的幂 + 冷热分离降规模 + 提前对齐排序字段。

**记忆口诀**：
- 分表 vs 分库：**表散文件 / 库散实例**
- 分区 vs 分表：**引擎内 / 逻辑层**
- 分片键五原则：**命中、均匀、不变、非空、高基数**
- 2 的幂：**翻倍恒定迁 50%**

## 常见误区

- **误区 1**：说"二进制所以选 2 的幂" → 正确：**取模扩容的数学性质**（`x % (2N)` 只影响一半数据）
- **误区 2**：把"分区"和"分表"当成一回事 → 正确：分区只能扩文件、扩不了实例；分表可以跨机扩
- **误区 3**：把"数据倾斜"只当成"分片键选错" → 正确：还有**扩容未重平衡**、**哈希冲突**、**热点 key**等来源
- **误区 4**：说"分库分表是万能的" → 正确：跨分片 JOIN、分布式事务、全局 ID、二次扩容都是代价，是**最后手段**
- **误区 5**：忽略分区对主键的强约束 → 正确：MySQL 要求分区键**必须包含在所有主键/唯一键里**，否则行归属不明确

## 延伸追问

1. **单库瓶颈和单表瓶颈有什么差别？**
   - 单库瓶颈是**实例级**（连接池/QPS/故障域），单表瓶颈是**存储级**（B+ 树深/文件大/DDL 阻塞）。解决手段不同：分库 vs 分表。
2. **分区表为什么必须把分区键放进主键？**
   - MySQL 要求唯一键必须包含分区键，否则无法确定行归属到哪个分区。所以按时间分区但主键是自增 ID 时，得把分区键并进主键。
3. **`DROP PARTITION` 为什么比 `DELETE` 快几个数量级？**
   - DROP PARTITION 直接 unlink 子文件，不做行级删除、不写 undo、不回收碎片。DELETE 要逐行处理 + purge 队列。
4. **一致性哈希为什么能只迁 1/N？**
   - 环形映射下扩容只在相邻节点之间迁移，物理节点从 N 变 N+1，只影响原本落在最后一段虚拟点的数据（约 1/N）。
5. **分片键选错线上怎么救？**
   - 双写新旧分片 → 全量回补 → 灰度切读 → 校验对账 → 反向迁移 → 下线旧分片。依赖 ShardingSphere 这类中间件的在线 DDL 能力。
6. **什么情况下不需要分库分表？**
   - 单机未瓶颈 + 有归档/裁剪需求 → 只用分区；QPS 未到瓶颈但数据量大 → 只读副本 + 缓存；业务有明确读写分离 → 先做垂直拆分。

## 速查表

```
分表 vs 分库:  表散文件 / 库散实例
分区 vs 分表:  引擎内文件 / 逻辑层表
分片键五原则:  命中 / 均匀 / 不变 / 非空 / 高基数
2 的幂扩容:    翻倍恒定迁 50%（N→N+1 迁 80%+）
位掩码:         hash(x) & (N-1)  仅 2 的幂等价 % N
触发阈值:      500w-2000w 行 / B+ 树 ≥4 层 / 连接池打满
倾斜两类:     数据量倾斜 / 访问倾斜（QPS）
治理三阶段:   事前选键 / 事中监控 / 事后治理
规模估算:     数据量÷增速=月数 → 冷分离 → 向上取 2 的幂
```

## 关联题目（题库）

- ✅ 《什么是分库？分表？分库分表？》— 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, ⭐⭐⭐（漏数学推导）

## 关联知识

- [分片算法与全局 ID 生成](./sharding-algorithms-id.md)
- [跨分片 JOIN 与分页方案](./sharding-join-pagination.md)
- [5 亿订单分库分表迁移](./sharding-migration.md)
- [MySQL MVCC 多版本并发控制](../mysql/mvcc.md)
- [MySQL 事务核心机制](../mysql/mysql-transaction-core.md)
- [主题地图](./_moc.md)

## Anki 候选卡片

1. **正**：分表 vs 分库的核心差别？**反**：分表分散数据文件（存储层），分库分散实例（连接池/QPS/故障域）
2. **正**：分区和分表的实现层次差异？**反**：分区在引擎层（同一实例内文件切分，SQL 透明）；分表在逻辑层（跨实例水平扩，需 SQL 路由）
3. **正**：分片键五大原则？**反**：①主查询命中 ②分布均匀 ③不可变 ④避免 NULL ⑤低基数陷阱
4. **正**：`x % (2N)` 与 `x % N` 的扩容关系？**反**：只影响 `x % N ≥ N` 的一半数据；翻倍扩容恒迁 50%
5. **正**：数据倾斜的两种类型？**反**：数据量倾斜（存储/IO 热点）+ 访问倾斜（QPS/热点 key）
6. **正**：触发分库分表的三个阈值？**反**：单表 500w-2000w 行、B+ 树高度 ≥4、单库连接数/QPS 打满

---

*最后更新：2026-09-23*
