MySQL 分库分表详解(含实践示例)

MySQL 分库分表详解(含实践示例)

十分想念顺店杂可。。。

分库分表是应对 MySQL 单库单表瓶颈的核心手段,通过拆分数据分散存储和访问压力。本文从拆分维度、实现方式、实践示例到关键挑战进行全面梳理,帮助理解和落地分库分表方案。

一、按拆分维度分类

1. 垂直分表(Column Sharding)

定义

将单表中字段按访问频率和关联性拆分到多个表,各表共享同一主键,业务上属于同一主体。

适用场景

单表字段过多(如超过 50 个),查询时加载冗余字段导致 IO 效率低;存在大字段(text、blob)或低频访问字段,拖慢核心查询;字段访问频率差异显著(如用户核心信息 vs 扩展资料)。

优缺点

优点缺点减少单表字段数,降低 IO 成本需维护多表关联,增加 join 操作核心字段与大字段分离,提升查询效率跨表查询增加业务复杂度表结构更贴合业务模块-

实践示例

-- 原用户表(字段冗余)

CREATE TABLE `user` (

`id` bigint PRIMARY KEY AUTO_INCREMENT,

`username` varchar(50) NOT NULL, -- 高频

`password` varchar(100) NOT NULL, -- 高频

`age` int, -- 高频

`address` text, -- 低频+大字段

`intro` text, -- 低频+大字段

`create_time` datetime NOT NULL

);

-- 拆分后:核心信息表(高频访问)

CREATE TABLE `user_core` (

`id` bigint PRIMARY KEY AUTO_INCREMENT,

`username` varchar(50) NOT NULL,

`password` varchar(100) NOT NULL,

`age` int,

`create_time` datetime NOT NULL

);

-- 拆分后:扩展信息表(低频访问)

CREATE TABLE `user_ext` (

`user_id` bigint PRIMARY KEY, -- 与user_core.id关联

`address` text,

`intro` text,

FOREIGN KEY (`user_id`) REFERENCES `user_core`(`id`) ON DELETE CASCADE

);

-- 插入数据(需同时写入两表)

INSERT INTO `user_core` (username, password, age, create_time)

VALUES ('zhangsan', 'hash_pwd', 25, '2023-01-01 08:00:00');

INSERT INTO `user_ext` (user_id, address, intro)

VALUES (LAST_INSERT_ID(), 'Beijing', 'A developer');

-- 查询用户完整信息(join两表)

SELECT c.*, e.address, e.intro

FROM `user_core` c

LEFT JOIN `user_ext` e ON c.id = e.user_id

WHERE c.username = 'zhangsan';

2. 垂直分库(Database Sharding by Business)

定义

将不同业务模块的数据库拆分到独立实例,各库无直接关联,按业务边界隔离。

适用场景

单库承载多业务,出现 CPU、内存或 IO 瓶颈;业务模块访问频率差异大(如订单库并发远高于商品库);业务耦合度低,可独立扩展(如用户、订单、商品模块)。

优缺点

优点缺点分散单库压力,各库可独立配置资源跨库业务需处理分布式事务业务隔离,某库故障不影响其他模块全局表(如字典表)需冗余维护便于按业务分工维护跨库 join 困难

实践示例

-- 原单库架构:all_in_one_db(包含用户、订单、商品表)

all_in_one_db

├─ user(用户表)

├─ order(订单表)

└─ product(商品表)

-- 垂直分库后:按业务拆分3个独立库

user_db(用户库)

└─ user(用户相关表)

order_db(订单库)

└─ order(订单相关表)

product_db(商品库)

└─ product(商品相关表)

-- 分库后,查询用户订单需跨库操作(伪代码示意)

-- 1. 从 user_db 查用户信息

SELECT * FROM user_db.user WHERE id = 1001;

-- 2. 从 order_db 查订单信息

SELECT * FROM order_db.order WHERE user_id = 1001;

3. 水平分表(Row Sharding)

定义

将单表中数据按规则拆分到多个结构相同的表,各表字段一致,数据不同(按行拆分)。

拆分规则

规则类型说明适用场景示例范围拆分按数值 / 时间范围拆分时间序列数据(日志、订单)订单表按月份分表:order_202301、order_202302哈希拆分按关键字哈希取模数据访问均匀(用户表)用户表按 user_id%4 分 4 表:user_0~user_3列表拆分按枚举值拆分数据有明确分类(地区、状态)订单表按地区分表:order_beijing、order_shanghai复合拆分结合多种规则复杂场景(大促订单)先按时间范围,再按用户 ID 哈希

适用场景

单表数据量过大(超 1000 万行),查询 / 索引维护效率低;单表写入压力高(每秒数千条 insert),磁盘 IO 瓶颈明显。

优缺点

优点缺点降低单表数据量,提升读写效率跨表查询需聚合结果(复杂)可按需扩展表数量分表规则变更需迁移全量数据各分表可独立优化(索引、存储)需维护路由逻辑

实践示例

示例 1:按时间范围分表(订单表)

-- 2023年1月订单表

CREATE TABLE `order_202301` (

`id` bigint PRIMARY KEY AUTO_INCREMENT,

`order_no` varchar(32) UNIQUE NOT NULL,

`user_id` bigint NOT NULL,

`amount` decimal(10,2) NOT NULL,

`create_time` datetime NOT NULL,

INDEX idx_user_id (`user_id`)

);

-- 2023年2月订单表(结构与order_202301一致)

CREATE TABLE `order_202302` (...);

-- 插入数据(按create_time路由到对应表)

INSERT INTO `order_202301` (order_no, user_id, amount, create_time)

VALUES ('ORD20230105001', 1001, 99.99, '2023-01-05 10:30:00');

示例 2:按哈希拆分(用户表)

-- 按user_id%4分4张表(user_0~user_3)

CREATE TABLE `user_0` (

`id` bigint PRIMARY KEY AUTO_INCREMENT,

`user_id` bigint UNIQUE NOT NULL, -- 哈希分表键

`username` varchar(50) NOT NULL,

`create_time` datetime NOT NULL

);

CREATE TABLE `user_1` (...); -- 结构与user_0一致

CREATE TABLE `user_2` (...);

CREATE TABLE `user_3` (...);

-- 插入数据(按user_id哈希路由)

-- user_id=1001:1001%4=1 → 插入user_1

INSERT INTO `user_1` (user_id, username, create_time)

VALUES (1001, 'zhangsan', '2023-01-01 08:00:00');

4. 水平分库(Database Sharding by Data)

定义

将水平分表后的表分布到多个数据库实例,同时分散库和表的压力(分库 + 分表结合)。

适用场景

水平分表后单库压力仍过大(CPU / 内存 / 连接数瓶颈);需支持更高并发(多库可同时处理更多请求)。

优缺点

优点缺点同时分散库和表的压力,支持高并发架构复杂,需管理多库多表路由可按库隔离资源(热点库单独配置)跨库事务和 join 难度大

实践示例

-- 分4个库,每个库含4张订单表(共4库×4表=16分片)

order_db_0(库0)

├─ order_0(user_id%16=0)

├─ order_1(user_id%16=1)

├─ order_2(user_id%16=2)

└─ order_3(user_id%16=3)

order_db_1(库1)

├─ order_4(user_id%16=4)

├─ order_5(user_id%16=5)

├─ order_6(user_id%16=6)

└─ order_7(user_id%16=7)

order_db_2(库2)→ 存储8-11分片

order_db_3(库3)→ 存储12-15分片

-- 路由规则:

-- 库索引 = user_id % 4

-- 表索引 = user_id % 16

-- 例:user_id=1001 → 1001%4=1(库1),1001%16=9(表9)

INSERT INTO `order_db_1.order_9` (order_no, user_id, amount)

VALUES ('ORD20230501001', 1001, 199.99);

二、按实现方式分类

1. 客户端方案(嵌入式分片)

原理

在应用中集成分片逻辑(如 JDBC 驱动扩展),直接路由请求到目标库表。

代表工具

Sharding-JDBC(Apache ShardingSphere)

实现流程

应用配置分片规则(分库键、分表键、路由算法);Sharding-JDBC 拦截 SQL,解析后路由到目标库表;聚合结果返回给应用。

优缺点

优点缺点无中间件,性能损耗低(仅 SQL 解析)与应用耦合,需在应用层维护规则架构简单(应用与数据库直连)多语言支持差(主要支持 Java)-扩容需重启应用更新配置

示例配置(Sharding-JDBC)

# 分片规则配置(简化版)

spring:

shardingsphere:

rules:

sharding:

tables:

t_order: # 逻辑表名

actual-data-nodes: order_db_${0..3}.order_${0..15} # 实际分片

database-strategy: # 分库策略

standard:

sharding-column: user_id

sharding-algorithm-name: order_db_inline

table-strategy: # 分表策略

standard:

sharding-column: user_id

sharding-algorithm-name: order_table_inline

sharding-algorithms:

order_db_inline: # 分库算法(user_id%4)

type: INLINE

props:

algorithm-expression: order_db_${user_id % 4}

order_table_inline: # 分表算法(user_id%16)

type: INLINE

props:

algorithm-expression: order_${user_id % 16}

2. 中间件方案(代理层分片)

原理

在应用与数据库间部署代理服务,统一处理路由、聚合、事务等逻辑。

代表工具

MyCat、ShardingSphere-Proxy、ProxySQL

实现流程

应用连接代理(如 MyCat),按单库单表方式写 SQL;中间件解析 SQL,按规则路由到目标库表;聚合结果返回给应用。

优缺点

优点缺点与应用解耦,支持多语言(标准 MySQL 协议)中间件可能成为性能瓶颈(需集群部署)集中管理分片规则,扩容无需改应用额外网络开销(应用→中间件→数据库)可扩展读写分离、容灾等功能-

示例(MyCat 逻辑表配置)

user_id

4

3. 数据库原生方案(分区表)

原理

MySQL 原生支持分区表,物理上拆分到多个文件,逻辑上为单表,数据库自动管理路由。

分区类型

范围分区(RANGE):按范围拆分(如时间、数值);列表分区(LIST):按枚举值拆分(如地区);哈希分区(HASH):按哈希值拆分;键分区(KEY):基于 MySQL 内部哈希函数。

优缺点

优点缺点对应用透明,无需修改代码仍属单库,无法分散实例压力(CPU / 内存瓶颈)维护简单(MySQL 自动管理)分区数有限制(过多影响性能)支持分区级操作(如删除历史分区)不支持跨分区全局索引

实践示例(RANGE 分区)

-- 订单表按季度分区

CREATE TABLE `order_partition` (

`id` bigint PRIMARY KEY AUTO_INCREMENT,

`order_no` varchar(32) UNIQUE NOT NULL,

`user_id` bigint NOT NULL,

`amount` decimal(10,2) NOT NULL,

`create_time` datetime NOT NULL,

INDEX idx_user_id (`user_id`)

)

PARTITION BY RANGE (TO_DAYS(create_time)) (

PARTITION p2023Q1 VALUES LESS THAN (TO_DAYS('2023-04-01')),

PARTITION p2023Q2 VALUES LESS THAN (TO_DAYS('2023-07-01')),

PARTITION p2023Q3 VALUES LESS THAN (TO_DAYS('2023-10-01')),

PARTITION p2023Q4 VALUES LESS THAN (TO_DAYS('2024-01-01'))

);

-- 插入数据(自动路由到对应分区)

INSERT INTO `order_partition` (order_no, user_id, amount, create_time)

VALUES ('ORD20230501001', 1001, 199.99, '2023-05-01 14:30:00'); -- 进入p2023Q2

-- 查询指定分区(优化性能)

SELECT * FROM `order_partition` PARTITION (p2023Q2)

WHERE user_id = 1001;

三、关键挑战与解决方案

挑战解决方案跨库 join1. 数据冗余(如订单表冗余商品名称);2. 应用层组装(先查 A 库,再查 B 库,内存 join);3. 中间件支持(MyCat 全局表、ER 表)分布式事务1. 最终一致性(消息队列 + 本地事务);2. 2PC 协议(Seata);3. TCC 补偿机制全局 ID1. 雪花算法(Snowflake);2. 数据库自增 ID 分段(ShardingSphere 分布式 ID);3. Redis 自增扩容迁移1. 双写迁移(旧库和新库同时写入,校验一致后切换);2. 中间件弹性伸缩(ShardingSphere)

四、总结

分库分表需结合业务场景选择方案:

字段多、访问频率差异大 → 垂直分表;业务模块独立、单库压力大 → 垂直分库;单表数据量过大(千万级 +) → 水平分表;分表后单库仍压力大 → 水平分库;轻量场景、不愿引入中间件 → MySQL 分区表;高并发、多语言 → 中间件方案(MyCat/ShardingSphere-Proxy);性能敏感、Java 应用 → 客户端方案(Sharding-JDBC)。

实际落地中,通常采用 “垂直分库 + 水平分表” 组合,并提前规划分片规则(预留扩容空间),结合中间件降低维护复杂度。

相关创意

谈谈代码混淆技术的原理、方法和未来
注册送365

谈谈代码混淆技术的原理、方法和未来

📅 07-26 👁️ 7411
《神武》一速职业天魔特技应该如何选择
注册送365

《神武》一速职业天魔特技应该如何选择

📅 07-29 👁️ 3426
lv手机外壳
注册送365

lv手机外壳

📅 08-10 👁️ 2300
如何解决苹果手表app安装不了的问题?
365bet体育在线官

如何解决苹果手表app安装不了的问题?

📅 08-15 👁️ 8281
如何正确地剪枝修剪桔子树——图解教程(从基础知识到实操技巧,让你成为桔子树的修剪专家)
拔智齿用什么工具
注册送365

拔智齿用什么工具

📅 07-02 👁️ 3434
怀孕期间,妈妈的子宫被撑大20倍!揭秘孕妇子宫变化全过程
巨人网络最出名的游戏有哪几个 2024热门的巨人网络游戏盘点