深色模式
分库分表入门
摘要:单库容量或写入成为瓶颈时才考虑分库分表。本文讲清垂直拆分与水平拆分的区别、分片键如何选、用 ShardingSphere-JDBC 做路由,以及分片带来的查询限制与扩容代价。
适用环境
bash
# 先确认真的到了必须拆的地步
mysql -uroot -p -e "SELECT @@max_connections;"
# 观察:单机 CPU/IO 是否长期高水位、单表数据量、写入 QPS1
2
3
2
3
sql
SELECT table_name, table_rows, ROUND(data_length/1024/1024/1024,2) AS data_gb
FROM information_schema.tables WHERE table_schema='shopdb' ORDER BY data_length DESC LIMIT 5;1
2
2
操作步骤
1. 先别急着拆:按顺序优化
- SQL 与索引优化(成本最低,收益最大)
- 缓存热点读、读写分离
- 归档冷数据(历史订单迁出)
- 垂直拆分(把大字段、独立业务拆到单独库)
- 最后才是水平分库分表
危险
分库分表会显著提高系统复杂度:分布式事务、跨片查询、扩容重分片都是长期负担。能用归档和索引解决的场景不要拆。
2. 拆分方式选择
| 方式 | 做法 | 解决什么 |
|---|---|---|
| 垂直分库 | 按业务拆(订单库、用户库) | 单库连接与容量压力 |
| 垂直分表 | 大字段拆到扩展表 | 单行过宽、热点字段分离 |
| 水平分表 | 同库内按规则拆成 orders_0…N | 单表过大、索引膨胀 |
| 水平分库分表 | 数据与压力分散到多库 | 单库写瓶颈 |
3. 分片键选择原则
- 选查询条件里最高频出现的字段(如
user_id、order_id)。 - 数据分布要均匀,避免热点(按时间分片常导致「今日库」过热)。
- 尽量让关联查询落在同一分片(如订单与订单明细都用
order_id分片)。
4. 常见分片算法
取模: shard = user_id % 8 # 分布均匀,扩容需迁移
范围: 按 id 区间或时间区间 # 易扩容,可能热点
一致性哈希: 扩容只迁移少量数据 # 实现复杂1
2
3
2
3
5. 用 ShardingSphere-JDBC 配置分片
yaml
# application.yml(示意)
spring:
shardingsphere:
rules:
sharding:
tables:
orders:
actual-data-nodes: ds$->{0..1}.orders_$->{0..3}
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: orders-mod
sharding-algorithms:
orders-mod:
type: MOD
props:
sharding-count: 8
datasource:
names: ds0,ds11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
6. 全局 ID 与分布式事务
- 主键不能用单机自增,改用雪花算法(Snowflake)或号段发号器。
- 跨分片更新属于分布式事务,优先通过「同分片设计」规避,其次用事务消息或最终一致性补偿。
验证
sql
-- 确认数据确实落到了不同分片
SELECT (SELECT COUNT(*) FROM ds0.orders_0) + (SELECT COUNT(*) FROM ds1.orders_3) AS sample;1
2
2
bash
# 通过路由执行,检查 SQL 是否被正确改写(开启 SQL 日志)
grep 'Actual SQL' app.log | tail -n 101
2
2
常见坑
WARNING
不带分片键的查询会广播到所有分片,性能极差;必须在上线前用 SQL 日志找出并改造这类查询。
WARNING
ORDER BY + LIMIT、跨片 GROUP BY、JOIN 在分片环境下语义与结果可能不符合预期,需由中间件归并,成本高。
DANGER
扩容分片数(如 8 → 16)需要数据重分布。上线前就要定好容量上限与重分片方案,否则扩容时会长期停写。