分库分表
- S:在学习和实践“分库分表”时,需要建立统一的概念和问题边界。
- C:相关概念、实现机制和工程取舍容易混在一起,导致只记住结论而无法判断适用条件。
- Q:“分库分表”要解决什么问题?它的核心抽象、工作机制和边界分别是什么?
- A:本文围绕“分库分表”整理核心概念、关键机制和工程实践,并明确其适用边界。
S 单库单表在数据量、索引规模、单主写入吞吐上都有上限;主从复制只能扩读,写请求最终还是落在主库。
C 一旦把数据拆到多个库表,事务、Join、分页、扩容、排障都会明显变复杂;拆错分片键,后面迁移成本极高。
Q 什么时候该做分库分表?它到底解决什么,不解决什么?分片键怎么选,拆完后最常见的代价和坑有哪些?
A 分库分表本质上是把单机数据库里的数据和写流量,按某个规则拆到多个分片上,以换取容量和写扩展;它解决的是单主写瓶颈和单表过大,但代价是把很多单机数据库内部问题暴露成分布式系统问题。
它和主从、分区是什么关系
- 主从复制:核心是
读写分离。主库写,从库读,主要解决读压力和高可用,不解决单主写瓶颈。 - 数据分区 / Sharding:更通用的概念,表示把数据拆到多个分片上。
- 分库分表:数据库场景里的分片落地方式。通常是
先按规则路由到某个库,再落到某张表。
可以把它记成:
主从解决读扩展分库分表解决写扩展和容量扩展
为什么要分库分表
- 单主写扛不住:写请求都打到一个主库,主从再多也没用。
- 单表太大:索引膨胀,查询和维护成本上升。
- 业务隔离需要:不同业务域独立扩容,减少相互影响。
但一般不是一上来就做,而是常先经过:
- 读写分离
- 缓存
- 业务拆分
- 最后才是分库分表
常见拆法
- 垂直拆分:按业务域拆库,比如用户库、订单库、支付库。主要解决模块隔离和业务独立扩展。
- 水平拆分:同一张表的数据按规则打散到多个库表,比如按
user_id % 8。主要解决容量和写吞吐。
典型形态:
业务 -> 路由层 -> 分片 1 (master + replicas)
-> 分片 2 (master + replicas)
-> 分片 3 (master + replicas)也就是说,很多系统不是“主从”或“分库分表”二选一,而是:
先分片每个分片内部再主从复制
分片键怎么选
分片键决定一条数据落到哪个库表,是分库分表最关键的设计点之一。
好的分片键通常要满足:
- 高基数:值足够分散,便于均匀打散写流量
- 稳定:创建后基本不变,避免数据搬家
- 查询常用:核心查询最好经常带这个字段,避免广播查询
- 贴近事务边界:一组经常一起读写的数据,尽量落在同一分片
- 不易形成热点:避免超级大客户、热门 key 把单分片打爆
常见候选:
user_idtenant_idorder_id
常见错误:
- 按低基数字段分片,如
status、gender - 按时间直接分片,导致新写集中到当天分片
- 只考虑写均衡,不考虑查询路由
分库分表后的典型代价
1. 跨库事务变复杂
单库里一个事务就能完成的多步更新,拆分后可能变成跨分片事务。
常见后果:
- 本地事务不够用
- 需要分布式事务,复杂且成本高
- 或改成最终一致性,比如消息队列 + 补偿
2. Join 变难
如果关联表不在同一分片:
- 跨库 Join 很难做或代价很高
- 往往需要应用层拼装
- 或通过字段冗余减少 Join
3. 全局分页和排序变难
order by ... limit ... 在单表里很自然;分表后通常要:
- 每个分片先查一部分
- 再在应用层归并排序
- 最后取全局前 N 条
4. 全局唯一和 ID 生成变难
单库自增主键不再天然适用,通常要引入:
- 雪花算法
- 号段模式
- UUID
同时,唯一索引也往往只能保证分片内唯一,不再天然保证全局唯一。
5. 扩容和再平衡麻烦
如果早期规则是 user_id % 4,后面扩成 8 个分片,就会涉及:
- 路由规则变化
- 历史数据迁移
- 双写、校验、切流
所以分片规则一旦落地,后面很难随意改。
6. 热点与数据倾斜
就算总体看分布均匀,也可能因为个别大客户或热门 key 导致:
- 某个分片特别热
- 其他分片相对空闲
这类问题本质上是逻辑均匀不等于流量均匀。
它适合解决什么,不适合解决什么
适合:
- 单主写吞吐不够
- 单表规模过大
- 明确知道主要访问模式,可以稳定选出分片键
不适合:
- 查询模式还不稳定
- 业务还在快速变化
- 当前瓶颈其实是读、慢 SQL、索引设计或缓存缺失
如果瓶颈还没定位清楚,先做分库分表,常常只是把一个单机问题升级成分布式问题。
和数据分区那篇的分工
- CS++数据分区 偏
通用分区原理:range、hash、热点、rebalancing、二级索引 - 本文偏
数据库工程实践:主从 vs 分库分表、垂直/水平拆分、分片键、事务/Join/分页/全局 ID
一句话总结:
分库分表不是“把表拆开”这么简单,而是在用更高的系统复杂度,去换更高的写吞吐和更大的数据规模。
评论