← 返回题目列表

分库分表怎么设计?分片键应该如何选择?

高频 困难 第 22 / 33 题 更新于 2026/07/28
数据库设计分库分表分片键水平拆分

简化版

分库分表是把数据按分片键拆到多个表或库,用来解决单表过大、单机容量和写入压力问题。分片键要高频出现在查询条件中、分布均匀、业务边界清晰,并且能支撑扩容;选错分片键会导致跨分片查询、热点和迁移困难。

详细版

分库分表要先回答几个问题:

  • 为什么拆:容量、写入、查询、归档还是组织边界?
  • 按什么拆:用户 ID、商家 ID、订单 ID、时间还是组合策略?
  • 怎么路由:应用层、代理层还是中间件?
  • 跨分片查询怎么办:广播、聚合、冗余索引表还是搜索系统?
  • 全局 ID 怎么生成:雪花 ID、号段、中心服务?
  • 扩容怎么做:预分片、再哈希、迁移工具?

好的分片键一般具备:

  • 查询经常带上;
  • 数据和请求分布均匀;
  • 不容易变化;
  • 能隔离业务边界;
  • 避免单分片热点。

完整版教学

一、分库分表是架构手术,不是普通优化

分库分表会改变数据访问方式。原来一条 SQL 能完成的查询,拆分后可能需要路由、聚合、排序、分页、事务补偿。

所以它通常是最后一类手段。能通过索引、冷热归档、读写分离、缓存、SQL 优化解决的问题,不要急着分库分表。

二、什么时候真的需要拆

常见信号:

  • 单表数据量过大,DDL、备份、查询都越来越重;
  • 单库写入吞吐达到瓶颈;
  • 存储容量逼近单机上限;
  • 热点业务和普通业务互相影响;
  • 数据天然按租户、用户、商家隔离;
  • 未来增长趋势明确超过单库承载能力。

如果只是某几条 SQL 慢,通常应该先查索引和 SQL,而不是拆库。

三、分片键决定系统命运

分片键是路由入口。比如订单按 user_id 分片,那么查某个用户订单很方便;但运营后台按商家、时间、状态查全站订单,就可能跨很多分片。

如果按 merchant_id 分片,商家后台方便,但用户维度查询可能变复杂。

没有完美分片键,只能围绕最核心、最高频、最不可妥协的访问路径选择。

四、常见分片策略

常见方式:

  • 哈希取模:按 user_id % N 分片,分布较均匀,但扩容迁移麻烦;
  • 范围分片:按时间或 ID 范围分片,便于归档,但可能有写热点;
  • 预分片:提前规划较多逻辑分片,再映射到物理库;
  • 组合分片:先按租户,再按时间或哈希拆。

选择策略时要同时考虑查询、写入、扩容和运维。

一个最简单的哈希路由公式是:

shard_no = hash(sharding_key) % shard_count

比如 user_id=10086shard_count=16 时,路由到哪个分片完全由计算结果决定。这个公式看起来简单,但扩容到 32 个分片时,大量 key 的取模结果会变化,所以生产系统常用逻辑分片层降低迁移范围。

五、跨分片问题怎么处理

分库分表后,最痛的是跨分片:

  • 跨分片 JOIN 很难;
  • 全局排序分页复杂;
  • 聚合统计成本高;
  • 跨分片事务成本高;
  • 唯一约束不再天然全局生效。

解决方式包括:冗余维度表、建立查询索引表、异步汇总、使用搜索引擎、OLAP 系统、最终一致事务方案。

六、扩容要提前设计

如果一开始按 user_id % 4 拆成 4 个库,后来要扩到 8 个,很多数据都要迁移。迁移期间还要处理双写、校验、回滚和流量切换。

更成熟的做法是引入逻辑分片层,比如 1024 个逻辑分片映射到少量物理库。扩容时移动一部分逻辑分片到新库,影响更可控。

分片前还要确认全局唯一约束怎么处理。单库里 unique(order_no) 很简单,拆成 32 个库后,如果订单号不是统一生成而是各库自增或各业务线自造,就可能出现跨分片重复。全局 ID、唯一索引表或发号服务要在拆分方案里提前设计。

七、常见误区与追问

问题拆分前能否先尝试拆分后必须补的能力
单条 SQL 慢索引、执行计划、SQL 改写分片路由也救不了坏 SQL
单表过大冷热归档、分区、历史表路由、迁移、校验
单库写入瓶颈批量写、削峰、缓存全局 ID、跨分片事务方案
多维查询搜索/OLAP/冗余索引表跨分片聚合与分页

易错点:分片键一旦选定,就会进入所有查询、扩容和数据迁移路径。它不是字段命名问题,而是系统未来几年的访问形态问题。

举个热点例子:如果按 merchant_id 分片,一个头部商家占全站 30% 订单,那么这个商家所在分片会持续热点;即使总共有 32 个分片,也不是每个分片平均承压。分片键不仅要让数据量均匀,还要让请求量尽量均匀。

  • 误区:表超过千万行就必须分库分表。 数据量只是信号之一,还要看行宽、索引、查询模式、写入压力、归档能力和硬件资源。
  • 误区:分片后查询一定更快。 如果查询不带分片键,反而可能广播到所有分片再聚合,延迟和复杂度更高。
  • 误区:哈希分片天然没有热点。 如果某个业务实体本身请求量极高,即使哈希均匀,也可能在单个实体所在分片形成热点。
  • 追问:分片键怎么选? 优先选择高频查询必带、分布均匀、稳定不变、能表达业务边界的字段。
  • 追问:跨分片分页为什么难? 每个分片都可能有候选数据,需要分别查询、全局归并排序,再计算下一页游标或偏移。
  • 追问:扩容为什么建议逻辑分片? 例如 1024 个逻辑分片映射到 4 个物理库,扩到 8 个库时只迁移部分逻辑分片,不必让所有数据重新取模。

八、加强记忆

分库分表解决容量和吞吐,但代价是查询、事务、扩容和运维复杂度暴涨。分片键要跟最高频访问路径一致,并尽量均匀稳定;拆之前先确认普通优化已经不够,拆之后要有路由、全局 ID、跨分片查询和扩容方案。