大促零点一过,下单按钮转了半天圈。原因可能有很多(网络、应用、锁竞争、热点行……),今天说其中一个:所有订单都挤在一台数据库、一张表里。这一期讲分库分表。
1. 为什么要拆:从库只分读,写还在主库
上一期讲过,读多了可以加从库。但每一笔下单都是写。单台机器的存储、计算和网络带宽都有上限;当写入量也很高时,只靠只读从库解决不了瓶颈。2 Vitess 的建议是:读压力可以加从库或拆分片,写压力只能拆分片。3
在此之前可以先纵向扩容(加盘、加内存、换 CPU),但这只能推迟问题。2
能不拆就不拆:如果数据量和吞吐(含预期增长)放得进单个实例,纵向扩容能保住查询的简单和事务的完整。2
2. 分表和分库
| 数据放在哪 | 谁负责找数据 | |
|---|---|---|
| 分表 | 同一个库里多张结构相同的物理表(如 order_0、order_1),由中间件或代码拼成一张逻辑表 | 应用 / 中间件 4 |
| 分库 | 多个数据库,通常在多台服务器 | 应用 / 中间件 23 |
水平拆分时,每个分片结构(schema)相同,但各自存一部分不重复的数据;同一条数据只在一个分片里。253 分片和主从复制是两回事,可以叠加:一个 Vitess 分片通常就是一个主库加若干从库。3
3. 水平拆分和垂直拆分
- 水平拆分(按行):同结构、不同行。被视为解决单机问题的标准方案。4
- 垂直拆分,有两种说法:
4. 分片键和路由
分片键是分片系统里最关键的设计决定;选定后再改,通常要把全部数据迁到新布局,在线上系统里代价高、风险大。2
好的分片键:不可变、基数高、能让数据和负载均匀分布,并且贴合主要查询,让多数请求只落到一个分片。按范围分片时,要避开单调递增的值(自增整数、顺序时间戳)做分片键,否则新数据全挤在最后一个分片,按哈希分片可以缓解;也要避开低基数属性。2 Vitess 要求主 Vindex 对同一输入只产生一个 keyspace ID,分片键插入后也不能改。6
中间件按分片键把查询路由到对应分片。3 SQL 里不带分片键、也没有辅助索引能定位分片时,就只能全路由(扫所有分片),性能差。46
分片策略
| 策略 | 怎么分 | 好处 | 代价 |
|---|---|---|---|
| 范围(range) | 按连续区间,如月份 | 查询范围落在一个分片内时,一个分片就能拿完(跨区间仍要查多个分片) | 最新数据最活跃时,集中在少数分片形成热点 2 |
| 哈希(hash) | 对分片键算哈希 | 降低热点概率,无需映射表 | 没有一致性哈希时再平衡困难;范围查询要跨片 2 |
| 查表(lookup) | 维护「键 → 分片」映射表 | 控制更灵活,配合虚拟分片便于迁移 | 查映射有额外开销,映射表需高度可缓存 2 |
5. 拆了之后的代价
- 跨片查询:跨多个分区的查询或事务,延迟和复杂度通常高于单分区操作。5 分页、排序、聚合分组在分片后容易处理出错。4
- 分布式事务:跨分片事务有挑战;两阶段提交等协调协议会增加延迟、引入新的失败方式。2
- 扩容 / 重新分片:再平衡要在分片间搬数据,常导致停机或吞吐下降;朴素取模在增减分片时会让大部分键换位置,一致性哈希能减小影响。2 Vitess 能在旧分片继续服务读写时,复制、校验并同步新分片。7
- 热点:数据倾斜会随时间累积,要定期再平衡。2 TiDB 文档也指出自增主键会形成写热点。1
- 全局唯一 ID:分片后,各处自己编号会撞号,给不同数据节点生成全局唯一主键很棘手。4
- 备份恢复:对单个分片做时间点恢复,可能和其他分片不一致。2
6. 和 MySQL 原生分区的区别
MySQL 原生分区(partitioning)把一张表的不同部分存成独立的物理部分,规则可以是取模、范围、值列表、内部哈希或线性哈希。8 但它仍在一台服务器里,应用看到的还是一张表;MySQL 8.4 只支持水平分区,不支持垂直分区,也没有计划支持;只有 InnoDB 和 NDB 支持分区。8 普通单机 MySQL(InnoDB)的分区仍在一台服务器里,分担不了这台机器的压力,不等于分库;NDB Cluster 会把分区分到多个数据节点,是另一回事。
7. 「单表两千万行就要拆」?
这是中文社区流传的经验说法,不是官方数字。MySQL 8.4 手册、Azure、Vitess、ShardingSphere 都没有给出「多少行该拆」的阈值;实际取决于硬件、索引和访问方式。
8. 现成方案(不做推荐)
- Apache ShardingSphere:以 JDBC 驱动或 Proxy 方式,让多个分片库用起来像一个库;概念有逻辑表、真实表、分片列。4
- Vitess:VTGate 路由、Vindex、在线重新分片。367
- TiDB:数据按 Region 自动拆分调度,应用不用自己分库分表,但仍需注意写热点。1
Footnotes
-
TiDB 文档,Troubleshoot Hotspot Issues,https://docs.pingcap.com/tidb/stable/troubleshoot-hot-spot-issues/ ,stable(v8.5 系),2026-10-15 抓取 ↩ ↩2 ↩3
-
Azure 架构中心,Sharding pattern,https://learn.microsoft.com/en-us/azure/architecture/patterns/sharding ,2026-04-02 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14
-
Vitess 文档,Sharding,https://vitess.io/docs/reference/features/sharding/ ,2026-04-14(v24.0) ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Apache ShardingSphere 文档,Sharding / Concept,https://shardingsphere.apache.org/document/current/en/features/sharding/ ,current 版(5.5.3),页面无日期,仅参考(村长决定降为辅助来源) ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Azure 架构中心,Data partitioning guidance,https://learn.microsoft.com/en-us/azure/architecture/best-practices/data-partitioning ,2023-11-15 ↩ ↩2 ↩3 ↩4
-
Vitess 文档,Vindexes,https://vitess.io/docs/reference/features/vindexes/ ,2026-07-15 ↩ ↩2 ↩3
-
Vitess 文档,Resharding,https://vitess.io/docs/user-guides/configuration-advanced/resharding/ ,2025-10-29 ↩ ↩2
-
MySQL 8.4 参考手册,26.1 Overview of Partitioning,https://dev.mysql.com/doc/refman/8.4/en/partitioning-overview.html ,2026-10-15 抓取 ↩ ↩2
-
MySQL 8.4 参考手册,26.6.1 Partitioning Keys, Primary Keys, and Unique Keys,https://dev.mysql.com/doc/refman/8.4/en/partitioning-limitations-partitioning-keys-unique-keys.html ,2026-10-15 抓取 ↩
-
MySQL 8.4 参考手册,26.6 Restrictions and Limitations on Partitioning,https://dev.mysql.com/doc/refman/8.4/en/partitioning-limitations.html ,2026-10-15 抓取 ↩
幕后
这部片子完全由代码生成。
系列 · 数据库第 6/6 期