分库分表
约 1390 字大约 5 分钟
布欧-Lewyon
2026-05-15
首页 › MySQL › 运维与部署(在新窗口打开) › 分库分表
当单表数据量达到千万甚至亿级时,即使做了索引优化,查询和写入也会遇到瓶颈。分库分表是突破单库瓶颈的关键手段。
什么时候需要分库分表
这不是绝对的阈值,具体取决于字段数量、写入频率和查询模式。一般建议单表不超过 2000 万行。
垂直拆分(Vertical Sharding)
按表/功能模块拆分到不同数据库:
优点:
- 各库独立部署,资源隔离。
- 按业务分团队维护。
- 可对不同业务使用不同数据库引擎。
缺点:
- 跨库 JOIN 不可用(需应用层做聚合)。
- 分布式事务(需 TCC / Saga)。
- 库间无法使用外键。
水平拆分(Horizontal Sharding)
将同一张表的数据按一定规则分到多个库/表的相同结构中。
取模分片
-- 按 user_id 取模分 4 张表
-- user_0: id % 4 == 0
-- user_1: id % 4 == 1
-- user_2: id % 4 == 2
-- user_3: id % 4 == 3
-- 查询:根据 user_id 取模确定表名
SELECT * FROM user_3 WHERE user_id = 7; -- 7 % 4 = 3范围分片
-- user_2024: id 1~1000000
-- user_2025: id 1000001~2000000
-- user_2026: id 2000001~3000000
-- 优点:扩容简单,加新表即可
-- 缺点:热点数据集中在新表哈希分片
-- 对分片键做哈希运算(一致性哈希环)
-- 优点:数据分布均匀,扩容影响小
-- 缺点:实现复杂度高分片键的选择
-- ❌ 不好的分片键:gender(只有 3 个值,分布极不均匀)
-- ❌ 不好的分片键:create_time(按时间范围,热点集中)
-- ✅ 好的分片键:user_id、order_id(高选择度、分布均匀)分片键选择原则:
| 原则 | 说明 |
|---|---|
| 高选择度 | 选择基数大、值分布均匀的列 |
| 覆盖主要查询 | 80% 的查询以分片键为条件 |
| 不可变 | 分片键一经确定不应更新 |
| 支持扩容 | 设计时考虑未来数据增长 |
分库分表的挑战
全局主键
-- 自增主键在分片下不唯一,需要全局 ID 生成方案
-- 方案一:雪花算法(Snowflake ID)
-- 方案二:Leaf(美团分布式 ID)
-- 方案三:UUID(但作为主键性能差)
-- 方案四:数据库号段(预分配 ID 区间)
-- 雪花 ID 示例(64 bit)
-- | 1 bit 符号位 | 41 bit 时间戳 | 10 bit 工作机器 | 12 bit 自增序列 |跨分片查询
-- 场景:按订单状态查询,而分片键是 user_id
-- 需要对所有分片执行查询后合并
SELECT * FROM order_0 WHERE status = 'PENDING'
UNION ALL
SELECT * FROM order_1 WHERE status = 'PENDING'
UNION ALL
SELECT * FROM order_2 WHERE status = 'PENDING'
UNION ALL
SELECT * FROM order_3 WHERE status = 'PENDING';扩容数据迁移
-- 从 4 个分片扩容到 8 个分片
-- 方式一:停服迁移(简单但影响业务)
-- 方式二:不停服迁移(双写 + 迁移 + 校验)
-- 不停服迁移流程:
-- 1. 启动新的分片库
-- 2. 应用双写(同时写旧分片和新分片)
-- 3. 迁移历史数据到新分片
-- 4. 校验数据一致性
-- 5. 切换到新分片,下线旧分片中间件方案
| 中间件 | 类型 | 特点 |
|---|---|---|
| ShardingSphere-JDBC | Java 应用内集成 | 轻量、无额外组件、性能好 |
| MyCat | 独立代理层 | 对应用透明,支持跨语言 |
| ProxySQL | 代理层 | 主打读写分离,轻量分片 |
| Vitess | 分布式数据库 | 基于 Kubernetes,云原生 |
ShardingSphere 配置示例:
# config-sharding.yaml
rules:
- !SHARDING
tables:
user:
actualDataNodes: ds0.user_$->{0..3}
tableStrategy:
standard:
shardingColumn: id
shardingAlgorithmName: user_id_mod
shardingAlgorithms:
user_id_mod:
type: MOD
props:
sharding-count: 4要不要分库分表
分库分表是最后的武器。在决定分库分表之前,先尝试:索引优化 → 读写分离 → 缓存(Redis)→ 归档历史数据。
小结
- 垂直拆分:按业务模块分库;水平拆分:按分片键将数据分布到同构表中。
- 分片键选择高区分度、不可变、覆盖主要查询的列。
- 分库分表引入的挑战:全局 ID、跨片查询、分布式事务、扩容迁移。
- 中间件方案:ShardingSphere-JDBC(应用内嵌)、MyCat/ProxySQL(代理层)。
- 分库分表不是必须的,优先尝试优化索引、缓存和归档。
