人人都会AI编程

27.1 分库分表适用场景与拆分策略

更新时间:2026-07-11

当一个业务从起步走向爆发,数据库迟早会撞上物理天花板。MySQL 单机性能有上限,单表行数过亿、数据文件上 TB 时,哪怕索引设计得再精巧,查询和写入都会明显变慢。分库分表就是为解决这类问题而生的架构手段。

什么时候该考虑分库分表

不是所有慢查询都需要上分库分表。很多时候,一把 EXPLAIN 和几次索引优化就能解决大半性能问题。但如果你的系统出现了以下几种信号,就该认真评估分库分表了:

  • 单表行数过大:InnoDB 的 B+ 树索引高度一般不超过 4 层,当表数据达到几千万甚至上亿时,索引依然可用,但维护代价陡增(如建索引耗时、DDL 变更困难)。一般以 2000 万 ~ 5000 万行作为单表行数的警戒线,超过后写入、查询、结构变更都会明显变慢。
  • 写入压力单机扛不住:单库的写入带宽受限于磁盘 IO 和 Redo Log 刷盘策略,当业务并发写入持续超标时,即使所有 SQL 都是最优的,CPU 和 IO 也会打满。此时必须把写入分到多个实例。
  • 单库容量过大:DB 文件动辄数百 GB、TB 级,备份恢复耗时极长,故障恢复窗口(RTO/RPO)难以保证。拆分后每个实例只有几十 GB,管理起来轻松很多。
  • 连接数过高:单台 MySQL 能维持的有效连接数有限(通常几百到两三千)。如果微服务实例过多导致连接池耗尽,拆分数据库到多个实例可以分散连接。
  • 业务逻辑清晰可以切分:不是所有数据都强关联,按业务域或租户天然隔离的数据非常适合拆分。

与之相反,有些情况不应该强行分库分表:数据量不大、写入不高、内部高度关联的多表查询非常多、团队尚未具备分布式运维能力等。分库分表会引入大量复杂度,因此在没遇到瓶颈之前,优先考虑优化、缓存、读写分离等手段。

拆分策略的核心思路

分库分表通常被拆成两个层面理解:

  • 分库(垂直分库 + 水平分库):将数据分布到不同的逻辑数据库甚至是不同的 MySQL 实例上。
  • 分表(垂直分表 + 水平分表):将一张表拆成多张表,既可以在同一个库里,也可以跨库。

实际架构中,这两者经常组合使用。大致策略可以归纳为垂直拆分和水平拆分两大类。

1. 垂直拆分(纵向拆分)

垂直拆分是把一张宽表按列拆开,或者把一个数据库里的表按业务域拆到不同库里。

垂直分表:指把一张表的列拆分到多张表中,常用方式是“冷热分离”或“主表 + 扩展表”。比如用户表包含常用字段(昵称、手机号、头像)和一些极少访问的大字段(个人简介、JSON扩展信息),可以把频繁查询的字段留在主表,把大字段、低频字段挪到扩展表。这样主表的行更紧凑,一页能存更多行,减少 I/O,对查询更友好。

垂直分库:指根据业务功能,把不同模块的表放到不同数据库或实例上。比如一个电商系统,拆成用户库(用户、地址)、商品库(商品、分类、SPU、SKU)、订单库(订单、订单明细)、支付库(流水、对账)。不同库之间甚至可以独立部署、独立扩容。垂直分库是最优先推荐的拆分方式,因为它成本相对低,逻辑清晰,耦合度可控。

2. 水平拆分(横向拆分)

水平拆分是把一张大表按某种规则,切分到多个结构相同的子表(或分库)里。每个子表包含原表的一部分行。

水平拆分的要点在于分片键(Sharding Key)的选择和分片算法的设计。

分片键选择原则

  • 查询频率最高:尽量让最常用的查询条件落在同一个分片上,避免跨分片查询。
  • 分布均匀:避免数据倾斜,比如按用户 ID 取模通常能均匀分布,按地区分片的话个别热门地区可能数据暴增。
  • 尽量是单一列,并且该列的值不可变(如用户 ID 而不是用户名,后者可能被修改导致数据需要迁移)。
  • 尊重业务特征:订单表按用户 ID 分片,那么用户查自己的订单就落在单分片;如果按订单 ID 分片,查询用户订单就要散布到所有库,压垮系统。

常见分片算法

  • 哈希取模分片序号 = hash(key) % N。实现简单,数据均匀,是使用最广的方案。缺点是一旦扩容(分片数 N 变化),大量数据需要重新分布,迁移成本高。
  • 一致性哈希:把分片节点映射到一个环形哈希空间,数据落在顺时针最近的节点上。扩容时只需要迁移部分数据,但实现稍复杂,且可能引入节点分布不均问题,可以通过虚拟节点优化。
  • 范围分片:按时间、ID 范围分区。比如 2019 年的数据在表1,2020 年在表2。扩容方便,也适合归档历史数据。缺点是可能产生热点写(新数据总是写入最新库),而冷数据库几乎无压力。
  • 地理/租户分片:按地理位置或租户 ID 分片,分布式部署时延迟更优。比如面向多租户的 SaaS,租户 A 的数据全在库 A,租户 B 在库 B,天然隔离。

分库分表带来的挑战

分库分表不是免费的午餐,它带来的复杂性主要体现在:

  • 分布式事务:原先同一个库内的 ACID 事务现在跨库了,需要使用 XA、TCC、消息队列等最终一致性方案,开发成本大幅上升。
  • 跨库 JOIN 和聚合查询:以前一条 SQL 能搞定的关联统计,现在需要应用层拼接,或者借助搜索引擎、数据仓库等专门组件。
  • 全局唯一 ID 生成:原先 AUTO_INCREMENT 主键不再可靠,需要雪花算法、自建发号器等方案保证 ID 全局唯一且趋势递增。
  • 数据迁移和扩容:当需要增加分片时,数据重分布是件头疼的事,需要工具、脚本和停服或灰度窗口。
  • 运维复杂度:实例变多,监控、备份、DDL 变更、故障恢复的对象从寥寥几个暴涨到几十上百个,必须工具化。

正因如此,分库分表往往是一个系统从小型走向大型过程中的“最后一公里”难题。但好在,业界已经有了大量成熟经验可供复用,比如 ShardingSphere、Vitess 等中间件,以及一系列久经考验的拆分方法和迁移方案。

总的来说,分库分表的总体思路是:垂直优先,水平按需;分片键决定命运;做好扩容预案。 在实际动手之前,也一定要评估业务是否真的需要这一步。当一切优化都到极限,数据增长依然无法阻挡,分库分表就是你下一道必须迈过的坎。