★ 分库分表怎么做?水平/垂直、取模/一致性哈希怎么选,代价是什么?
结论先行:分库分表是应对单库容量与写入吞吐上限的最后手段,核心是选好切分维度与路由算法。垂直拆分先做(按业务/字段拆),水平拆分后做(按行拆);路由常用取模与一致性哈希,各有扩容与热点的取舍;同时要清醒认识到它会带来跨库 join、分布式事务、分布式 id、排序分页等新问题。
一、先拆什么:垂直 vs 水平
| 维度 | 垂直拆分 | 水平拆分 |
|---|
| 拆法 | 按业务模块/按列拆 | 按行拆到多个库表 |
| 例子 | 订单库与用户库分开;大字段拆出 | user_0 ~ user_15 |
| 解决 | 字段多、模块耦合、单库连接数 | 单表数据量、单库写入吞吐 |
| 局限 | 不解决单表行数膨胀 | 非路由键查询不友好 |
二、路由算法怎么选
| 算法 | 规则 | 优点 | 缺点 |
|---|
| 范围分片 | 按 id/时间区间 | 易扩容、利于范围查询 | 热点集中在新区间 |
| 取模 | id % N 定库表 | 均匀、实现简单 | 扩容需迁移重分布 |
| 一致性哈希 | 环形空间 + 虚拟节点 | 增减节点只影响少量数据 | 实现复杂、可能不均 |
取模: 表号 = shard_key % 16
一致性哈希:shard_key 哈希后落到环上最近节点,虚拟节点摊平倾斜
三、必须提前想好的新问题
- 跨库 join 与跨库事务:需要应用层聚合或引入分布式事务方案。
- 分布式 id:不能依赖自增,常用雪花算法或号段模式。
- 全局排序分页:LIMIT 需各分片取回后内存归并,深分页代价大。
- 扩容迁移:取模扩容要双写 + 迁移工具,或翻倍扩容(2N)降低复杂度。
- 热点数据:爆款行仍会集中到一个分片,需在其上叠加缓存/缓冲。
- 全局唯一性:订单号、流水号等业务键要全局唯一设计,不能依赖分片内自增。
常见追问与记忆点
- 追问:什么量级该考虑分库分表?没有铁律,单表几千万行或单库写入成瓶颈且索引/缓存已到位时再评估。
- 追问:分片键选错了怎么办?只能选最核心的查询维度(订单号、用户 id),其它维度靠映射表/冗余/ES 兜底。
- 追问:分库后分布式 id 怎么生成?雪花算法或号段模式二选一,要求全局唯一且尽量趋势递增。
- 追问:先分库还是先分表?先分库解决连接与吞吐,再分表解决单表数据量,设计时一步到位减少二次迁移。
- 记忆点:垂直先拆字段与模块,水平再拆行;取模均匀难扩容、一致性哈希易扩容有倾斜风险;买的是容量,付的是复杂度。