当业务数据量增长到单机数据库无法承载时,SQL数据库的拆分是常见的优化手段,其中水平拆分和垂直拆分是最主流的两种方案,二者的设计思路和适用场景存在明显差异,需要结合实际情况做取舍。

什么是垂直拆分
垂直拆分是按照业务模块或者字段维度对数据库进行拆分,核心思路是把一个包含多张表或者多字段的大库/大表,拆分成多个关联度较低的独立部分。
垂直拆分的常见实现方式
垂直拆分主要分为两种类型:
- 垂直分库:把原本存储在同一个数据库中的不同业务模块表拆分到不同的数据库实例中,比如把用户相关表放到用户库,订单相关表放到订单库。
- 垂直分表:把同一张表中字段较多的表,按照访问频率拆分,把高频访问的字段放到主表,低频访问的字段放到扩展表,两张表通过主键关联。
垂直拆分示例
以用户表垂直分表为例,原始用户表包含id、username、password、email、address、reg_time等字段,拆分后主表存储高频访问的字段:
-- 用户主表,存储高频访问字段
CREATE TABLE user_main (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100),
reg_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 用户扩展表,存储低频访问字段
CREATE TABLE user_ext (
user_id INT PRIMARY KEY,
address VARCHAR(200),
phone VARCHAR(20),
FOREIGN KEY (user_id) REFERENCES user_main(id)
);
什么是水平拆分
水平拆分是按照数据行维度对数据库进行拆分,核心思路是把同一张表中的数据,按照某个规则(比如用户ID取模、时间范围)分散存储到多个结构相同的表或者数据库实例中。
水平拆分的常见规则
- 取模规则:按照主键或者拆分键的值对分表数量取模,比如user_id % 4,根据结果放到对应的4张分表中。
- 范围规则:按照时间或者ID范围拆分,比如2024年1月的订单放到order_202401表,2024年2月的订单放到order_202402表。
- 哈希规则:对拆分键做哈希计算后分配到不同的分表,降低数据倾斜的概率。
水平拆分示例
以用户表水平拆分为4张分表为例,拆分键为用户ID,取模规则为user_id % 4:
-- 分表1,存储user_id % 4 = 0的数据
CREATE TABLE user_0 (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100)
);
-- 分表2,存储user_id % 4 = 1的数据
CREATE TABLE user_1 (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100)
);
-- 分表3,存储user_id % 4 = 2的数据
CREATE TABLE user_2 (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100)
);
-- 分表4,存储user_id % 4 = 3的数据
CREATE TABLE user_3 (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100)
);
两种拆分方式的核心差异对比
从多个维度对比水平拆分和垂直拆分的差异,可以更清晰地判断二者的适用场景:
| 对比维度 | 垂直拆分 | 水平拆分 |
|---|---|---|
| 拆分维度 | 列/业务模块维度 | 行维度 |
| 表结构 | 拆分后表结构不同 | 拆分后表结构完全相同 |
| 适用场景 | 业务模块耦合度低、单表字段过多 | 单表数据量过大、访问QPS过高 |
| 实现复杂度 | 较低,基本不需要额外的中间件支持 | 较高,通常需要分库分表中间件支持 |
| 扩容难度 | 扩容时只需新增对应业务库即可 | 扩容需要迁移存量数据,难度较大 |
| 跨拆分查询 | 跨库关联查询需要额外处理 | 跨分表查询需要聚合结果,复杂度高 |
如何做取舍
在实际架构设计中,不需要非此即彼,通常可以按照以下优先级做决策:
优先判断是否需要垂直拆分
如果业务存在明显的模块划分,或者单表字段过多(比如超过30个字段),且不同字段的访问频率差异大,优先做垂直拆分。垂直拆分可以天然隔离不同业务的数据库资源,避免某个业务的高负载影响其他业务,同时实现成本较低,不需要引入复杂的中间件。
垂直拆分后仍有瓶颈再考虑水平拆分
如果做完垂直拆分后,单个业务模块的单表数据量仍然超过千万级,或者单表的读写QPS过高导致数据库负载过高,此时再考虑水平拆分。水平拆分可以突破单表的数据量上限,提升单表的读写性能,但需要提前做好拆分键的选择,避免后续扩容时数据迁移成本过高。
复杂场景可以结合使用
对于大型业务系统,通常会先按业务做垂直分库,再对数据量大的核心表做水平分表,二者结合使用。比如电商系统先拆分为用户库、订单库、商品库,再对订单库中的订单表按照时间范围做水平拆分,既隔离了业务资源,又解决了单表数据量过大的问题。
注意事项
无论选择哪种拆分方式,都需要注意以下问题:
- 拆分前先做好数据评估,明确当前数据量、增长趋势、访问热点,避免过早拆分增加维护成本。
- 垂直拆分时要避免跨库事务,尽量保证单个业务操作只涉及单个数据库,降低事务复杂度。
- 水平拆分时要选择合适的拆分键,优先选择查询频率高、区分度大的字段作为拆分键,避免数据倾斜。
- 拆分后尽量避免跨拆分维度的join查询,如果必须跨维度查询,可以考虑在应用层做数据聚合,或者引入搜索引擎、数据仓库等组件做离线分析。
数据库拆分是手段不是目的,核心是解决当前业务的性能瓶颈,不要为了拆分而拆分,需要结合业务发展阶段做合理的架构设计。