分库分表

2026-04-271 出链2 引用
  • S:在学习和实践“分库分表”时,需要建立统一的概念和问题边界。
  • C:相关概念、实现机制和工程取舍容易混在一起,导致只记住结论而无法判断适用条件。
  • Q:“分库分表”要解决什么问题?它的核心抽象、工作机制和边界分别是什么?
  • A:本文围绕“分库分表”整理核心概念、关键机制和工程实践,并明确其适用边界。

S 单库单表在数据量、索引规模、单主写入吞吐上都有上限;主从复制只能扩读,写请求最终还是落在主库。 C 一旦把数据拆到多个库表,事务、Join、分页、扩容、排障都会明显变复杂;拆错分片键,后面迁移成本极高。 Q 什么时候该做分库分表?它到底解决什么,不解决什么?分片键怎么选,拆完后最常见的代价和坑有哪些? A 分库分表本质上是把单机数据库里的数据和写流量,按某个规则拆到多个分片上,以换取容量和写扩展;它解决的是单主写瓶颈单表过大,但代价是把很多单机数据库内部问题暴露成分布式系统问题。

它和主从、分区是什么关系

  • 主从复制:核心是读写分离。主库写,从库读,主要解决读压力和高可用,不解决单主写瓶颈。
  • 数据分区 / Sharding:更通用的概念,表示把数据拆到多个分片上。
  • 分库分表:数据库场景里的分片落地方式。通常是先按规则路由到某个库,再落到某张表

可以把它记成:

  • 主从解决读扩展
  • 分库分表解决写扩展和容量扩展

为什么要分库分表

  • 单主写扛不住:写请求都打到一个主库,主从再多也没用。
  • 单表太大:索引膨胀,查询和维护成本上升。
  • 业务隔离需要:不同业务域独立扩容,减少相互影响。

但一般不是一上来就做,而是常先经过:

  1. 读写分离
  2. 缓存
  3. 业务拆分
  4. 最后才是分库分表

常见拆法

  • 垂直拆分:按业务域拆库,比如用户库、订单库、支付库。主要解决模块隔离和业务独立扩展。
  • 水平拆分:同一张表的数据按规则打散到多个库表,比如按 user_id % 8。主要解决容量和写吞吐。

典型形态:

text
业务 -> 路由层 -> 分片 1 (master + replicas)
               -> 分片 2 (master + replicas)
               -> 分片 3 (master + replicas)

也就是说,很多系统不是“主从”或“分库分表”二选一,而是:

  • 先分片
  • 每个分片内部再主从复制

分片键怎么选

分片键决定一条数据落到哪个库表,是分库分表最关键的设计点之一。

好的分片键通常要满足:

  • 高基数:值足够分散,便于均匀打散写流量
  • 稳定:创建后基本不变,避免数据搬家
  • 查询常用:核心查询最好经常带这个字段,避免广播查询
  • 贴近事务边界:一组经常一起读写的数据,尽量落在同一分片
  • 不易形成热点:避免超级大客户、热门 key 把单分片打爆

常见候选:

  • user_id
  • tenant_id
  • order_id

常见错误:

  • 按低基数字段分片,如 statusgender
  • 按时间直接分片,导致新写集中到当天分片
  • 只考虑写均衡,不考虑查询路由

分库分表后的典型代价

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

一句话总结:

分库分表不是“把表拆开”这么简单,而是在用更高的系统复杂度,去换更高的写吞吐和更大的数据规模。

评论