SQL分库分表是针对单数据库实例或者单数据表数据量过大、性能不足的问题,将原本存储在一个库、一张表的数据拆分到多个数据库实例、多张数据表中的数据库优化方案,目的是降低单节点的存储和查询压力,提升系统的整体吞吐量。

分库分表的核心分类
分库分表按照拆分维度可以分为垂直拆分和水平拆分两大类,两种拆分方式的适用场景和设计逻辑有明显区别。
垂直拆分
垂直拆分又分为垂直分库和垂直分表两种实现方式,核心逻辑是按照业务模块或者字段属性拆分数据。
垂直分库
垂直分库是按照业务功能模块拆分数据库,把不同业务模块的数据放到不同的数据库实例中。比如电商系统中,可以把用户相关的数据放到用户库,订单相关的数据放到订单库,商品相关的数据放到商品库。这种方式可以隔离不同业务的数据库压力,避免单个业务的高负载影响其他业务。
垂直分表
垂直分表是针对单张表的字段过多的情况,把字段按照访问频率、业务属性拆分到多张表中。比如用户表有用户基础信息、用户扩展信息、用户隐私信息三类字段,基础信息访问频率最高,扩展信息次之,隐私信息访问频率最低,就可以拆分为用户基础表、用户扩展表、用户隐私表,三张表通过用户ID关联。垂直分表可以减少单表的字段数量,提升查询时的IO效率。
水平拆分
水平拆分同样分为水平分库和水平分表,核心逻辑是按照数据行的维度,把同一张表的数据按照一定规则拆分到多个库或者多张表中,所有拆分后的表结构完全一致。
水平分库
水平分库是把同一张表的数据按照拆分规则分到不同的数据库实例中,每个库中的表结构相同,但是存储的数据不同。比如订单表数据量达到千万级,就可以把订单数据拆分到10个订单库中,每个库存储不同范围的订单数据。
水平分表
水平分表是在同一个数据库实例中,把一张表的数据拆分到多张结构相同的表中。比如单库中的订单表数据量过大,查询变慢,就可以在同一个库中拆分为订单表_0、订单表_1等多张表,分散单表的存储和查询压力。
常见的水平拆分规则
水平拆分需要选择合适的拆分规则,保证数据分布均匀,避免数据倾斜,常见的拆分规则有以下几种:
- 范围拆分:按照某个字段的范围拆分数据,比如按照用户ID范围,1-100万的用户数据放到表0,100万-200万的用户数据放到表1,以此类推。这种方式规则简单,后期扩容方便,但是容易出现热点数据问题,比如新注册的用户ID都集中在最新的表中,导致最新表的负载过高。
- 哈希拆分:对拆分字段做哈希运算,然后对分表数量取模,根据结果决定数据存储的表。比如用户ID对8取模,结果为0的存到表0,结果为1的存到表1,以此类推。这种方式数据分布比较均匀,但是扩容时需要迁移大量数据。
- 时间拆分:按照时间维度拆分数据,比如订单表按照月份拆分,2024年1月的订单存到订单表_202401,2024年2月的订单存到订单表_202402。这种方式适合有明显时间属性的数据,查询时可以根据时间范围快速定位到对应的表,但是历史数据的查询需要跨多表。
分库分表落地注意事项
分库分表虽然能解决性能问题,但是也会带来一些新的复杂度,落地时需要注意以下几点:
- 选择合适的拆分时机,不要过早拆分,一般在单表数据量超过千万级、查询性能明显下降时再考虑拆分。
- 拆分规则要提前规划,尽量选择数据分布均匀的字段作为拆分键,避免后续修改拆分规则带来的大量数据迁移成本。
- 分库分表后跨库跨表的查询、事务处理会变得更复杂,需要引入对应的中间件或者框架支持,比如ShardingSphere、MyCAT等。
- 要考虑扩容方案,水平拆分后如果数据量继续增长,需要提前规划好扩容时的数据迁移方案,尽量减少对业务的影响。
简单示例:水平分表的实现
以下是一个简单的用户表水平分表的SQL示例,假设按照用户ID哈希取模拆分到4张表中:
-- 创建4张用户分表,表结构完全一致
CREATE TABLE user_0 (
user_id INT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
user_age INT,
create_time DATETIME
);
CREATE TABLE user_1 (
user_id INT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
user_age INT,
create_time DATETIME
);
CREATE TABLE user_2 (
user_id INT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
user_age INT,
create_time DATETIME
);
CREATE TABLE user_3 (
user_id INT PRIMARY KEY,
user_name VARCHAR(50) NOT NULL,
user_age INT,
create_time DATETIME
);
-- 插入数据时根据user_id取模选择对应的表
-- 假设插入user_id为1001的用户,1001 % 4 = 1,插入user_1表
INSERT INTO user_1 (user_id, user_name, user_age, create_time) VALUES (1001, '张三', 25, NOW());
-- 查询user_id为1001的用户时,同样先取模定位到user_1表再查询
SELECT * FROM user_1 WHERE user_id = 1001;
以上就是SQL分库分表的基础概念和常见拆分策略的讲解,开发者可以根据自身业务的数据量、访问特点选择合适的拆分方案,平衡性能收益和架构复杂度。