← 返回文章

分库分表

发布于 2026/10/17

1. 完整回答

分库分表是将原来集中在一个数据库或一张表中的数据,按照业务或者一定的分片规则拆分到多个数据库、多个数据表中,主要解决单库单表数据量过大、读写压力过高以及存储容量不足的问题。

分库分表主要分为垂直拆分和水平拆分。

垂直拆分是按照业务或者字段进行拆分,比如把用户、订单、商品拆到不同的数据库,或者把一张表中不常访问的大字段拆到扩展表。

水平拆分是把同一张表的数据按照某个字段分散到不同的库表,比如订单表按照用户 ID 进行哈希分片,让不同用户的订单分布在不同的数据库中。

它的核心机制是分片键、分片算法和数据路由。

应用发起 SQL 后,分片中间件会解析查询条件,根据分片键计算目标库表,将 SQL 路由到对应的数据库执行。如果涉及多个分片,还需要对结果进行归并、排序或聚合。

常见的实现方案有 ShardingSphere-JDBC 和 ShardingSphere-Proxy。

分库分表最大的优势是能够分散数据存储和访问压力,实现数据库水平扩展。但同时也会引入跨库关联查询、分布式事务、全局唯一 ID、分页排序以及数据迁移等复杂问题。

实际项目中,通常会先优化索引、SQL、缓存和读写分离。当单库的容量或写入能力仍然存在明显瓶颈时,再考虑分库分表,比较适合订单、交易流水、日志等数据量持续增长的业务。

2. 核心原理与关键概念

2.1 核心原理

分库分表的核心是通过分片规则确定数据位置,并对 SQL 进行路由和执行。

整体流程:

SQL 解析 → 分片路由 → SQL 改写 → SQL 执行 → 结果归并

  1. SQL 解析:解析 SQL 中的表名、查询条件和分片键。

  2. 分片路由:根据分片算法计算需要访问的数据库和数据表。

  3. SQL 改写:将逻辑表名改成实际表名,必要时调整分页等 SQL 内容。

  4. SQL 执行:在目标分片执行 SQL,多个分片可以并行执行。

  5. 结果归并:如果查询涉及多个分片,需要合并结果,并完成排序、聚合或分页。

例如,将订单数据按照用户 ID 分成 4 张表:

tableIndex = hash(userId) % 4

用户查询自己的订单时,根据用户 ID 就能定位到对应的订单表。

如果 SQL 没有分片键,也没有其他可用的路由信息,就可能需要访问所有分片,再合并结果。

因此,分片键能否覆盖主要查询场景,直接影响分库分表后的性能。

2.2 关键概念

概念 含义
分片键 决定数据存储位置的字段,例如用户 ID、订单 ID
分片算法 计算数据应该进入哪个分片的规则,例如哈希、取模、范围分片
逻辑表 应用访问的统一表名,例如 t_order
物理表 实际存储数据的表,例如 t_order_0、t_order_1
数据路由 根据分片规则定位需要访问的库表
绑定表 具有相同分片规则、可以实现分片内关联的表,例如订单表和订单明细表
广播表 在多个数据库中保存相同数据的小表,例如地区字典表

3. 高频面试追问

3.1 什么情况下需要分库分表?

当单库的存储容量、写入能力或者单表数据规模已经成为明显瓶颈,并且常规优化难以解决时,可以考虑分库分表。

主要看三个方面:

  • 数据规模:单表数据持续增长,索引维护、查询、备份或数据清理成本明显增加。

  • 访问压力:单库 CPU、磁盘 I/O、连接数或者写入吞吐量长期接近瓶颈。

  • 扩展需求:业务还会持续增长,需要通过增加数据库节点分担压力。

实际判断时,应该先分析慢 SQL、执行计划和数据库监控指标,排除索引不合理、深分页、缓存使用不当等问题。

没有统一的分表行数阈值。即使单表达到千万级数据,只要性能和容量满足业务要求,也可以继续使用单表。

3.2 分片键应该怎么选择?常见的分片算法有哪些?

分片键优先选择查询频繁、分布均匀、取值稳定,并且能够让相关业务数据落在同一个分片的字段。

例如订单系统:

如果主要场景是用户查询自己的订单,可以选择用户 ID 作为分片键,让同一个用户的订单集中在一个分片,方便查询和分页。

常见分片算法有两种:

  • 哈希分片:通过哈希和取模分配数据,通常分布比较均匀,适合按键查询,但范围查询和扩容比较麻烦。

  • 范围分片:按照时间、ID 区间分配数据,方便范围查询和历史数据管理,但容易出现热点。

分片键的选择需要结合业务查询方式。比如按用户 ID 分片后,仅凭订单 ID 查询订单,就需要额外的路由机制。

3.3 分库分表后,查询条件没有分片键怎么办?

没有分片键时,可以通过路由映射表、辅助索引或者冗余查询数据解决,尽量避免全分片扫描。

例如订单按照用户 ID 分片,但支付回调只有订单 ID。

可以维护一张 order_id → user_id 的映射表,先通过订单 ID 找到用户 ID,再根据用户 ID 路由到对应分片。

另一种方式是在生成订单 ID 时编码路由信息,查询时直接解析目标分片。

对于复杂搜索,可以将数据同步到 Elasticsearch 等检索系统。

如果没有可用的路由信息,通常就需要访问多个甚至全部分片。随着分片数量增加,查询成本会明显上升。

3.4 分库分表后,跨库 JOIN 怎么解决?

最优先的方案是让经常关联的数据落在同一个分片,其次再考虑业务层关联或数据冗余。

比如订单表和订单明细表,可以采用相同的分片键和分片规则,让同一订单的数据存储在相同数据库中,这样仍然可以使用普通 JOIN。

如果数据已经分散到不同数据库,可以分别查询,再在应用层组装结果。

对于高频关联查询,还可以建立冗余字段、宽表或者专门的查询模型。

ShardingSphere 的绑定表能够优化符合分片规则的关联查询,但需要满足相应的分片条件。

跨分片 JOIN 通常会增加网络传输和结果归并成本,因此表之间的关联关系最好在分片设计阶段就考虑清楚。

3.5 分库分表后,如何保证主键全局唯一?

通常通过分布式 ID 生成方案保证全局唯一,常见方案包括 Snowflake 算法和数据库号段模式。

Snowflake 算法一般由时间戳、机器标识和序列号组成,可以在不同节点上独立生成 ID,吞吐量比较高,但需要处理机器标识冲突和时钟回拨问题。

号段模式则是从数据库批量申请一段 ID,在应用本地生成后续 ID,可以减少频繁访问数据库的开销。

数据库自增主键只能直接保证对应表内的唯一性,多库分别自增可能产生重复值。

另外,全局唯一 ID 和全局唯一业务字段是两个问题。

继续追问:分库分表后,如何保证手机号全局唯一?

如果手机号分散在不同分片,单个数据库的唯一索引只能约束本分片。

可以建立以手机号为唯一键的全局索引表,在注册时进行原子占用,并结合事务或者补偿机制维护一致性。也可以根据业务设计,让相同手机号始终路由到同一个分片进行唯一性校验。

3.6 分库分表后,分页、排序和聚合怎么处理?

单分片查询可以正常使用数据库的分页和排序;跨分片查询需要分别执行,再统一归并结果。

例如查询所有订单,按照创建时间排序并获取第 1001~1010 条记录。

如果数据分布在多个分片,可能需要各分片分别返回前 1010 条候选记录,然后在中间件统一排序,截取最终结果。

分片越多、分页越深,读取和归并的成本通常越高。

常见优化方式包括:

  • 优先利用分片键,把查询限制在单个分片。

  • 使用游标分页,通过创建时间和订单 ID 等稳定排序字段定位下一页。

  • 对复杂统计、全局排序和报表查询建立专门的查询模型或分析系统。

COUNT、SUM 等聚合可以对分片结果进行合并,AVG 通常需要根据各分片的 SUM 和 COUNT 计算。

3.7 分库分表后,分布式事务怎么解决?

优先通过业务设计让事务落在单个数据库中;必须跨库时,再根据一致性要求选择分布式事务方案。

例如订单主表和明细表采用相同的分片规则,让同一笔订单的数据落在一个数据库中,就可以使用本地事务。

如果事务跨越多个数据库,常见方案有:

  • XA 两阶段提交:通过事务协调器统一提交或回滚多个数据库事务,适合一致性要求较高的场景,但会增加延迟和资源占用。

  • 可靠消息与最终一致性:通过本地消息表、事务消息、重试和幂等处理,使不同服务或数据库最终达到一致状态。

  • TCC、Saga:通过业务预留、确认、取消或补偿机制协调多个操作,适合业务流程较复杂的场景。

例如订单创建和积分发放,可以通过可靠消息实现最终一致性,并提供失败重试和幂等保障。

具体选择取决于业务能否接受短暂不一致,以及对延迟、可用性和开发复杂度的要求。

3.8 分库分表后,如何扩容?怎样不停机迁移数据?

扩容的核心是调整数据路由,并安全地把受影响的数据迁移到新分片。

如果直接使用 hash(key) % N,分片数量发生变化时,大量数据的路由结果也会改变,因此直接修改取模数通常需要迁移大量数据。

一种常见方案是提前划分较多逻辑分片,再建立逻辑分片到物理数据库的映射。扩容时只迁移部分逻辑分片,并调整映射关系。

不停机迁移通常按照以下流程:

建立新库表 → 全量数据迁移 → 增量数据同步 → 数据一致性校验 → 灰度切换路由 → 观察并清理旧数据

增量同步可以利用 MySQL Binlog 或数据库迁移工具实现。

切换阶段需要保证写入路由正确,避免重复写入、数据遗漏以及新旧路由读写不一致,同时准备回滚方案。

3.9 分库分表后,出现数据倾斜或热点怎么办?

首先区分数据量倾斜和访问热点,再根据原因调整分片策略。

数据量倾斜指某些分片的数据远多于其他分片。常见原因是分片键分布不均匀,比如按照地区分片,但不同地区的用户数量差异很大。

访问热点指少数分片承受大量请求。例如按照商家 ID 分片,大商家的订单可能集中在一个分片。

常见处理方式有:

  • 使用分布更均匀的哈希分片策略。

  • 对热点读请求使用缓存,降低数据库访问压力。

  • 将大租户或热点业务单独拆分。

  • 对超大租户增加更细粒度的分片维度。

需要注意,普通哈希可以改善不同分片键之间的数据分布,但如果大量请求都集中在同一个分片键上,单纯增加分片数量通常无法消除热点。

4. 易混淆概念对比

4.1 水平拆分和垂直拆分

对比维度 水平拆分 垂直拆分
拆分依据 按数据行拆分 按业务或字段拆分
数据结构 各分片表结构基本相同 不同表或库的数据结构可以不同
主要作用 分散数据量和访问压力 降低业务耦合,拆分宽表
典型例子 订单按用户 ID 拆分 用户库、订单库独立部署

4.2 分库和分表

对比维度 分库 分表
拆分对象 数据库 数据表
主要作用 分散数据库实例的存储和负载 控制单表数据规模
扩展能力 部署在不同实例时可以水平扩展 同一实例内分表仍受实例资源限制
主要代价 跨库事务、运维复杂度 跨表查询、路由和结果归并

实际场景中,水平分库和水平分表经常组合使用。

4.3 哈希分片和范围分片

对比维度 哈希分片 范围分片
分片依据 哈希值或取模结果 时间、ID 等范围
数据分布 通常比较均匀 可能存在热点
等值查询 容易定位分片 容易定位分片
范围查询 可能涉及多个分片 可以按范围缩小查询范围
扩容特点 直接改变取模数可能引发大量迁移 可增加或调整范围,但可能需要迁移已有区间

4.4 分库分表和数据库分区

对比维度 分库分表 数据库分区
实现方式 业务或中间件管理数据分片 数据库内部管理表分区
数据位置 可以分布在不同数据库实例 普通 MySQL InnoDB 分区属于同一数据库实例
扩展能力 可以分散实例的存储和处理压力 主要优化数据组织、分区裁剪和维护
查询处理 可能需要中间件进行路由和归并 数据库优化器负责分区访问

数据库分区适合时间范围查询、历史数据清理等场景;跨实例分库分表更适合单机容量或负载已经成为瓶颈的场景。

4.5 分库分表和读写分离

对比维度 分库分表 读写分离
核心方式 将不同数据分散到不同库表 将读请求分配给只读副本
主要解决 数据规模、存储和读写扩展 读请求压力
写入能力 跨实例分片可以分散写入压力 写请求通常仍由主库处理
主要问题 分布式事务、跨分片查询、扩容 主从复制延迟、数据一致性

两种方案可以结合使用,例如每个分库再部署自己的主从副本。

5. 总结

分库分表主要用于解决单库单表的数据规模、存储容量和访问压力瓶颈。 它可以按照业务或字段进行垂直拆分,也可以通过分片键和分片算法进行水平拆分。核心流程是解析 SQL、计算路由、执行查询,再对跨分片结果进行归并。它能够分散数据和访问压力,并支持通过增加数据库实例实现水平扩展,但也会带来跨库事务、关联查询、分页排序和数据迁移等问题。设计时最关键的是选择合适的分片键,让主要查询和事务尽可能落在单个分片,同时提前考虑数据分布和扩容方案。实际项目中,通常会先完成索引、SQL 和读写架构优化,再根据真实容量和性能瓶颈决定是否引入分库分表。