Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql分库、分表、分区

MySQL分库、分表、分区全攻略

作者:Gent_倪

‌分库、分表、分区是MySQL应对数据量增长的三种不同拆解手段,核心区别在于:分区是MySQL内部机制、对应用透明;分表和分库通常需要借助中间件或应用层改造,本文介绍MySQL分库、分表、分区全攻略,感兴趣的朋友一起看看吧

一、分区 Partitioning

        分区表是将一张大表在物理上分成多个分区(文件),但逻辑上仍是一张表,由MySQL内部管理。写入表的数据会通过MySQL的分区函数来决定存储在哪个分区中。

1. Range分区

         ‌RANGE 分区‌就是按某个列的值的连续区间,把数据分到不同的物理分区里。它最适合用在‌日期/时间字段‌上,比如按年、按月拆分历史数据,查询和清理数据都会快很多。‌可以使用 MINVALUE 和 MAXVALUE 设置范围的最小和最大值。

CREATE TABLE employees (
    id INT NOT NULL,
    store_id INT NOT NULL
) PARTITION BY RANGE (store_id) (
    PARTITION p0 VALUES LESS THAN (6),    -- 存 store_id 1~5
    PARTITION p1 VALUES LESS THAN (11),   -- 存 store_id 6~10
    PARTITION p2 VALUES LESS THAN (16),   -- 存 store_id 11~15
    PARTITION p3 VALUES LESS THAN MAXVALUE -- 存 >= 16 的所有数据
);

2. List分区

        LIST分区是‌按离散的枚举值列表‌来分配数据的,每个分区对应一组明确指定的值,而不是连续的区间。‌‌注意:RANGE按连续区间分,LIST按‌值列表‌分。

CREATE TABLE employees (
    id INT NOT NULL,
    store_id INT
) PARTITION BY LIST (store_id) (
    PARTITION pNorth VALUES IN (3, 5, 6, 9, 17),
    PARTITION pEast VALUES IN (1, 2, 10, 11, 19, 20),
    PARTITION pWest VALUES IN (4, 12, 13, 14, 18),
    PARTITION pCentral VALUES IN (7, 8, 15, 16)
);

3. HASH分区

        HASH分区主要用于把数据‌均匀地分散到预定数量的分区里‌,你不需要手动指定每条数据存哪个分区,MySQL会自动根据分区键的哈希值来分配。‌HASH分区的核心是‌取模运算‌:分区编号 N = MOD(expr, num)

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    store_id INT
) PARTITION BY HASH(store_id)
PARTITIONS 4;

4. KEY分区

    KEY分区使用MySQL服务器提供的内部哈希函数。这个函数会处理各种数据类型(如字符串、日期等),确保它们能被均匀地映射到各个分区。

与HASH分区的区别:HASH分区让你自己决定“如何计算哈希值”,而KEY分区则把这个任务交给了MySQL服务器。

特性HASH 分区KEY 分区
哈希函数用户定义的表达式MySQL服务器提供的内部哈希函数
分区键一个整数表达式或列一个或多个列名,且列不限于整数或NULL值
语法关键字PARTITION BY HASH (expr)PARTITION BY KEY (column_list)
与主键/唯一键关系分区表达式中的列必须包含在表的所有唯一键(包括主键)中分区键必须是表主键的一部分或全部;若未指定列,则默认使用主键
CREATE TABLE k1 (
    id INT NOT NULL PRIMARY KEY,
    name VARCHAR(20)
) PARTITION BY KEY() PARTITIONS 2; -- 不指定分区键,自动用主键

二、垂直分表(冷热数据分离)

        MySQL的垂直分表本质就是冷热数据分离:按字段访问频率,把一张大表拆成“热表”和“冷表”两张一对一的小表,通过主键关联。

举例:有一张订单表(单裤单表),字段如下:

-- 表结构(只列关键字段)
orders (
    order_id BIGINT AUTO_INCREMENT PRIMARY KEY,  -- 自增主键
    user_id INT,
    order_no VARCHAR(32),
    amount DECIMAL(10,2),
    create_time DATETIME,
    remark TEXT,        -- 买家备注(很长,但很少查)
    ... 
)
-- 主表(热数据,频繁查询)
orders ( order_id, user_id, order_no, amount, create_time ) 
-- 扩展表(冷数据,偶尔查)
orders_ext ( order_id, remark, delivery_address ) 

注意事项:

三、垂直分库(业务拆分)

        垂直分库就是‌按业务模块把不同的表拆到不同的数据库里‌。比如把用户相关的表放进用户库,商品相关的表放进商品库,订单相关的表放进订单库,每个库各管一摊业务。‌

优缺点:

四、水平分表

        水平分表是将一张大表的数据按行拆分到多个结构相同的子表中,用于解决单表数据量过大导致的性能瓶颈 。逻辑上仍视为一张表,但物理存储分散 。‌‌

一句话定义:把一张“胖大”的逻辑表,按行拆成多张结构完全相同的“瘦小”物理表,存放在同一个数据库实例(同一台服务器)中。

五、水平分库

一句话定义:把一张“胖大”的逻辑表,按行拆成多张结构完全相同的物理表,并分布到不同的数据库实例(多台服务器)上。

六、🛒 实战案例:订单表order的分库分表

背景:我们的订单表每天新增 50 万条,一年接近 2 亿条。查询基本都是 WHERE user_id = xxx。

架构设计:我们规划 4 个库(物理机),每个库里 4 张表,总共 16 张物理表。

1. 分片键:选择 user_id(用户ID)。因为 99% 的查询都是“我的订单”。

2. 路由算法:采用 user_id % 4(取模)。
(简单起见,库序号和表序号用同一个规则,即 user_id=1 的数据全进 库1 的 表1)

3. 写入演示(INSERT)
用户 user_id = 10086 下了一笔新订单。

4. 查询演示(SELECT)
该用户点击“我的订单”:SELECT * FROM order WHERE user_id = 10086 ORDER BY create_time DESC LIMIT 10。

5. 翻车现场(不带分片键)
运营后台要查:SELECT * FROM order WHERE create_time > '2026-01-01'。
因为没有 user_id,中间件不知道去哪查。它会向 4 个库的 16 张表同时发起查询,拿到结果后在内存里归并排序。这就是分库分表最怕的场景,会导致查询极慢甚至内存溢出。

七、总结对比

对比维度水平分库水平分表垂直分库垂直分表分区(Partition)
一句话定义按行将数据拆分到不同数据库实例(多台服务器) 的相同表中按行将数据拆分到同一数据库实例的多张相同结构表中按业务模块,将不同表拆分到不同数据库实例按字段访问频率,将一张宽表拆成多张窄表(同一库)按行将数据拆分到同一张逻辑表下的多个物理文件片段中
拆分依据数据行(基于分片键如 user_id)数据行(基于分片键如 user_id)业务表(如订单表、用户表、商品表)字段列(冷热字段分离)数据行(基于分区表达式如 HASH(id))
存储位置跨服务器(多台物理机/虚拟机)单服务器(同一个MySQL实例)跨服务器(不同业务库在不同机器)单服务器(同一个库中)单服务器(同一实例的同一数据库目录下)
对应用透明性不透明
需中间件或应用层改写SQL路由
不透明
需中间件或应用层改写表名
不透明
需应用层管理多数据源,无法跨库JOIN
半透明
需应用层知道要关联查扩展表
完全透明
应用写的还是原表名,MySQL内部自动路由
解决的核心痛点突破单机物理极限
(磁盘、CPU、内存、连接数、IOPS)
降低单表数据量
(压减B+Tree层级,提升索引效率)
业务解耦
(微服务化,资源隔离,互不抢占)
缩小行宽度
(提高热点数据缓存命中率,减少IO)
方便管理海量冷数据
(可快速删除分区文件)且查询时可裁剪分区
主要代价/痛点分布式事务难;
跨库JOIN失效;
深分页极慢;
扩容复杂
表总数膨胀;
仍需解决单机资源瓶颈;
跨表查询需中间件聚合
无法跨库联表查询;
需在应用层组装数据;
事务一致性难保证
查询常需关联两张表;
增加了代码复杂度
单机资源瓶颈依然存在;
对主键/唯一键有致命限制(分区列必须在所有唯一键中)
典型适用场景高并发C端核心表(如订单、支付、用户)单表数据量巨大但并发尚可,且暂时不想引入多机房分库中大型微服务系统(订单库、库存库、用户库分离)行数据包含大字段(如 TEXT、BLOB 的备注、详情)日志流水表、历史归档表(按天/月分区,便于快速清理)
扩容能力水平扩展(强)
加机器即可提升吞吐量
垂直扩展(弱)
仅能换更强硬件,无法加机器
水平/垂直混合
可按业务分配不同性能机器
几乎无
仍依赖单机性能
无
无法突破单机物理上限
主键/唯一键约束较宽松
中间件通常支持全局ID(如雪花算法),不强制要求分片键包含在唯一键中(视中间件具体实现而定)
较宽松
同上(通常依赖中间件管理唯一性)
无特殊限制
库不同,主键可独立自增
无特殊限制
依然是单表,主键自增正常
极其严格
若表有主键/唯一键,分区表达式涉及的所有列必须包含在每一个唯一键中

到此这篇关于MySQL分库、分表、分区全攻略的文章就介绍到这了,更多相关mysql分库、分表、分区内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文