一、先讲一个常见误区
很多同学在面试或方案设计中,一提到数据库性能瓶颈,第一反应就是:
"那我们分库分表吧。"
但在真实项目中,过早分库分表往往是灾难的开始。
- SQL 变复杂
- 事务难处理
- 运维成本陡增
- 很多查询被迫扫全表
所以我的观点很明确:
能不分就不分,分库分表应该是"最后的选择",而不是"第一反应"。
二、什么时候才真的需要分库分表?
先问自己几个问题:
✅ 1️⃣ 单表数据量真的很大了吗?
- 一般经验值:千万级开始考虑
- 如果索引合理、SQL 规范,千万级以内大多能扛住
✅ 2️⃣ 索引 + SQL 优化已经做到位了吗?
- 慢查询是否已全部分析?
- 是否存在大量 filesort / 临时表?
- 冷热数据是否分离?
✅ 3️⃣ 读写分离还能解决问题吗?
- 读多写少:优先读写分离
- 写多读少:才可能真的需要分表
✅ 4️⃣ 业务增长趋势是否明确?
- 短期峰值?还是长期线性增长?
- 一次性活动 ≠ 架构重构理由
只有当以上都"否",才进入分库分表的讨论。
三、分库分表到底分的是什么?
很多人以为分库分表就是"把表拆开",其实要分两层看:
1️⃣ 分库(Database Sharding)
- 不同业务库拆分(订单库 / 用户库 / 商品库)
- 解决连接数、CPU、IO 瓶颈
- 对业务侵入较小
2️⃣ 分表(Table Sharding)
- 同一张表按规则拆成多张子表
- 如:
orders_01~orders_32 - 解决单表数据量过大问题
四、分库分表一定会改 SQL 吗?
这是最多人担心的问题,也是最大的误解来源。
❌ 错误认知
"分表就要改所有 SQL,工作量太大。"
✅ 正确做法:使用中间件
主流方案是引入 分库分表中间件,例如:
| 中间件 | 特点 |
|---|---|
| ShardingSphere | Apache 顶级项目,支持 JDBC / Proxy |
| MyCat | 老牌中间件,偏运维 |
| Vitess | YouTube 出品,云原生友好 |
核心思想:逻辑表 vs 物理表
sql
-- 业务代码中
SELECT * FROM orders WHERE user_id = 1001;
sql
-- 实际执行
SELECT * FROM orders_05 WHERE user_id = 1001;
✅ 业务层只关心逻辑表
✅ 中间件负责路由
✅ 90% 的 CRUD SQL 不需要修改
五、分库分表的"隐形代价"
你一定要知道,分库分表不是免费午餐:
❌ 1️⃣ 跨分片查询性能差
ini
SELECT * FROM orders WHERE merchant_id = 10;
- 如果不带分片键(如 user_id)
- 会触发 广播查询:扫所有分表
- 性能甚至可能比单表还差
✅ 所以:分片键选择至关重要
❌ 2️⃣ 分布式事务复杂
-
跨库更新难以保证原子性
-
常用妥协方案:
- 最终一致性
- 事务消息(MQ)
- TCC / Saga 模式
❌ 3️⃣ 扩容困难
- 早期分片规则不合理
- 后期扩容需要数据迁移
- 往往伴随停机或复杂双写
❌ 4️⃣ JOIN 和聚合受限
- 跨分片 JOIN 基本不可行
- 统计类需求(SUM / COUNT)需要二次汇总
- 很多场景需要引入 ES / ClickHouse
六、我的实际决策流程
在真实项目中,我通常按这个顺序来:
1️⃣ SQL + 索引优化
- EXPLAIN 分析
- 覆盖索引
- 避免索引失效
2️⃣ 冷热数据分离
- 历史数据归档
- 热数据保留在 MySQL
3️⃣ 读写分离
- 主库写,从库读
- 缓解读压力
4️⃣ 引入缓存
- Redis 挡一层
- 减少 DB 查询
5️⃣ 最后才考虑分库分表
- 明确分片键
- 使用中间件降低侵入
- 做好容量规划
七、总结
分库分表是一种能力 ,而不是一种本能。
它不是性能的"速效救心丸",而是架构的"重型手术"。
记住一句话:
能靠索引解决的,别动表结构;能靠读写分离的,别分库;能靠中间件的,别手写分片逻辑。
附:面试速记点
- 分库分表是最后手段
- 中间件屏蔽分表细节
- 分片键决定生死
- 非分片键查询 = 全表扫描
- 分布式事务 = 最终一致性