为什么需要分库分表,如何实现?
0. 引言
读写分离解决了读压力,但写压力和单表数据量仍有天花板:单库写入 QPS 有限、单表过亿后索引深度与写入性能恶化。分库分表把数据按维度拆开——分库分散写压力,分表控制单表数据量。它是数据库扩展的终极手段,也是代价最高的手段(分布式事务、跨库查询、全局主键、扩容都随之而来)。本文讲清"何时该拆、怎么拆、拆完怎么办"。
1. 什么时候需要分库分表
text
单库连接数/写入 QPS 接近上限(如写 QPS > 5000)
单表数据量过大(如 > 2000 万 ~ 5000 万行,索引 B+ 树层数增加、DDL 锁表时间长)
单表存储空间过大(> 100GB,备份/迁移困难)拆分的顺序与克制:先缓存 → 再读写分离 → 再分库分表。分库分表是最后手段——它带来的复杂度(事务、Join、聚合、扩容)远超前两者。
2. 拆分方式
2.1 垂直拆分(按业务/字段)
| 类型 | 做法 | 示例 |
|---|---|---|
| 垂直分库 | 按业务域拆到不同库:用户库、订单库、商品库 | 订单表拆到订单库 |
| 垂直分表 | 大宽表拆成"常用字段表 + 扩展字段表" | 商品表拆出商品详情表 |
- 优点:业务隔离清晰、单库压力下降;
- 缺点:跨库事务(订单+库存)、跨库 Join 变应用层组装。
2.2 水平拆分(按数据行)
| 类型 | 做法 | 示例 |
|---|---|---|
| 水平分表 | 同一库内按分片键拆成多张表 | order_0 ~ order_9 |
| 水平分库 | 数据分布到多个库实例 | db_0 ~ db_3,各库内表结构相同 |
| 分库分表 | 先分库再分表(库表双维度) | db_0.order_0 ~ db_3.order_9 |
图表渲染中…
3. 分片键与分片算法
3.1 分片键选择(核心决策)
| 要求 | 说明 |
|---|---|
| 高频查询条件 | 查询必须带分片键才能精确定位(否则全库扫描) |
| 数据分布均匀 | 避免热点分片(如按用户 ID 而非订单总额) |
| 不可变或极少变 | 分片键变更 = 数据迁移 |
典型选择:订单表按
order_id或user_id分片;若按 user_id 分片,则"查某用户订单"天然命中单分片,但"按订单查"需广播。
3.2 常见分片算法
| 算法 | 实现 | 优点 | 缺点 |
|---|---|---|---|
| 取模 | shard = id % N | 简单、分布均匀 | 扩容翻倍要全量迁移(N 变化) |
| 范围 | 按 ID/时间区间分片 | 扩容友好(追加新区间) | 数据倾斜(热点集中在新区) |
| 哈希 | hash(id) % N | 分布均匀 | 同取模,扩容难 |
| 一致性哈希 | 哈希环 + 虚拟节点 | 扩容只影响少量数据 | 实现复杂 |
| 基因法 | 子表 ID 蕴含分片信息(如买家 ID 末位) | 无需额外映射表 | 设计约束多 |
4. 中间件选型
| 中间件 | 形态 | 特点 |
|---|---|---|
| ShardingSphere-JDBC | 应用内 SDK | 性能最好、功能全(分片+读写分离+事务);侵入应用 |
| ShardingSphere-Proxy | 独立代理 | 零侵入、多语言;多一跳 |
| MyCat | 独立代理 | 老牌、SQL 解析能力强;社区活跃度下降 |
| Vitess | K8s 原生(CNCF) | 大规模、云原生(YouTube 出品);复杂度高 |
ShardingSphere 生态:JDBC(客户端)与 Proxy(服务端)可混用(Proxy 聚合多库),配合分布式事务(Seata)、分布式主键、数据迁移(ShardingSphere Migration)形成完整方案。
5. 拆分后的"代价清单"
| 问题 | 影响 | 解法 |
|---|---|---|
| 跨库查询/Join | 无法单库 SQL 完成 | 冗余字段、应用层组装、宽表/汇总表 |
| 分布式事务 | 跨库写不一致 | 柔性事务(TCC/Seata/消息),详见事务篇 |
| 全局唯一主键 | 自增主键冲突 | 雪花算法/号段模式(详见下章) |
| 聚合查询/排序分页 | 需全分片聚合 | 数据异构到 ES/ClickHouse 分析 |
| 扩容 | 分片数变化需迁移 | 一致性哈希/双写迁移(详见"如何实现扩容") |
| 分布式 ID 与关联 | 业务键非分片键时定位难 | 索引表/基因法 |
6. 小结
- 拆分的顺序:缓存 → 读写分离 → 分库分表,分库分表是最后手段;
- 垂直拆(按业务/字段)解决隔离,水平拆(按分片键)解决容量;
- 分片键决定一切:高频查询维度的均匀字段;算法选型在"均匀性"与"扩容成本"间权衡;
- 中间件:ShardingSphere-JDBC(性能)/ Proxy(零侵入)/ Vitess(云原生);
- 拆分前先评估代价:事务、Join、聚合、主键、扩容——拆了还能合回去吗?
下一章解决拆分后的第一个难题:分布式唯一主键方案(雪花算法与号段模式)。