MySQL 表分区详解

MySQL 分区表是把一张大表按照规则拆分成多个物理小块(分区),逻辑上还是一张完整的表,对上层应用透明,不用改业务SQL。

核心目的:提升查询性能、降低维护成本、方便数据归档清理。

一、分区表基础概念

  1. 核心原理

逻辑层:一张完整的表,用户操作和普通表无区别

物理层:数据、索引分散存储在多个分区文件中,不同分区独立管理

分区键:用来划分数据的字段/表达式,是分区的核心

  1. 分区适用场景

表数据量极大(千万/亿级),单表IO压力大

按时间范围归档数据(日志、订单、流水表)

冷热数据分离,频繁查询热数据,冷数据单独存储

按地区、类别拆分数据,缩小查询扫描范围

  1. 不适合分区的场景

表数据量小,分区反而增加开销

频繁跨分区查询,会触发全分区扫描,性能下降

分区键频繁更新,会导致分区数据迁移,开销巨大

大量随机插入,分区不均匀导致热点分区

二、MySQL支持的分区类型

MySQL 5.1+ 支持4种主流分区,InnoDB、MyISAM 均支持:

  1. 范围分区 RANGE(最常用,时间分区首选)

按照字段值的连续范围划分分区,比如按时间、ID区间。

语法:

CREATE TABLE order(

id BIGINT,

create_time DATETIME

) ENGINE=InnoDB

PARTITION BY RANGE (TO_DAYS(create_time))(

PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),

PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')),

PARTITION p_max VALUES LESS THAN MAXVALUE

);

特点:

适合时间维度数据,按月/按天分表

清理旧数据直接删除分区,速度极快(ALTER TABLE order DROP PARTITION p202601;)

必须有上限分区 MAXVALUE,否则超出范围数据无法插入

  1. 列表分区 LIST

按照字段的离散枚举值划分,比如按地区、业务线、状态。

CREATE TABLE user(

id INT,

city VARCHAR(20)

) ENGINE=InnoDB

PARTITION BY LIST (city)(

PARTITION p_north VALUES IN ('北京','天津','河北'),

PARTITION p_south VALUES IN ('广东','广西','海南'),

PARTITION p_east VALUES IN ('上海','江苏','浙江')

);

注意:MySQL8.0 之前 LIST 分区不支持多字段枚举,只能单个字段。

  1. 哈希分区 HASH

根据分区键的哈希值取模,均匀分散数据,避免数据倾斜。

CREATE TABLE goods(

id INT,

stock INT

) ENGINE=InnoDB

PARTITION BY HASH(id)

PARTITIONS 8; -- 分成8个分区

变种:线性哈希分区 LINEAR HASH,采用线性算法,分区扩容/缩容更高效,但数据分散性略差。

  1. 键分区 KEY

类似哈希分区,MySQL自动对分区键做哈希运算,支持多字段分区。

CREATE TABLE log(

id BIGINT,

user_id INT

) ENGINE=InnoDB

PARTITION BY KEY(id,user_id)

PARTITIONS 6;

  1. 复合分区(子分区)

先按范围分区,再在每个范围分区内做哈希/列表二次分区,适合超大表。

例:先按时间范围,再按用户ID哈希

CREATE TABLE big_log(

id BIGINT,

create_time DATETIME,

user_id INT

) ENGINE=InnoDB

PARTITION BY RANGE (TO_DAYS(create_time))

SUBPARTITION BY HASH(user_id)

SUBPARTITIONS 4(

PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),

PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01'))

);

三、分区表关键语法操作

  1. 查看分区信息

-- 查看表分区状态

SELECT PARTITION_NAME,TABLE_ROWS,DATA_LENGTH,INDEX_LENGTH

FROM INFORMATION_SCHEMA.PARTITIONS

WHERE TABLE_SCHEMA='库名' AND TABLE_NAME='表名';

-- 查看分区定义

SHOW CREATE TABLE 表名;

  1. 新增分区(范围分区扩容)

ALTER TABLE order

REORGANIZE PARTITION p_max INTO (

PARTITION p202603 VALUES LESS THAN (TO_DAYS('2026-04-01')),

PARTITION p_max VALUES LESS THAN MAXVALUE

);

  1. 删除分区(清理历史数据)

ALTER TABLE order DROP PARTITION p202601;

风险:删除分区会直接删除对应数据,不可逆,InnoDB会立即释放磁盘空间。

  1. 合并/拆分分区

-- 拆分分区

ALTER TABLE t REORGANIZE PARTITION p_old INTO (PARTITION p1 VALUES LESS THAN 1000,PARTITION p2 VALUES LESS THAN 2000);

-- 合并分区

ALTER TABLE t REORGANIZE PARTITION p1,p2 INTO (PARTITION p_old VALUES LESS THAN 2000);

  1. 移除分区(变回普通表)

ALTER TABLE 表名 REMOVE PARTITIONING;

四、分区表工作原理:分区裁剪 Partition Pruning

这是分区表性能提升的核心机制。

当SQL的WHERE条件命中分区键时,MySQL优化器会只扫描符合条件的分区,跳过无关分区,大幅减少IO。

示例:

SELECT * FROM order WHERE create_time BETWEEN '2026-02-01' AND '2026-02-28';

优化器只会扫描 p202602 分区,不会扫描其他月份分区。

注意:如果分区键函数写法不对,会导致分区裁剪失效,全表扫描:

错误:WHERE DATE(create_time) = '2026-02-01'(函数作用在分区键上,优化器无法预判)

正确:WHERE create_time >= '2026-02-01' AND create_time < '2026-03-01'

五、分区表索引机制

分区索引:每个分区拥有独立的索引文件,索引是分区级别的,不是全局索引

普通二级索引:只在当前分区内生效,查询跨分区时需要扫描所有分区索引

InnoDB 分区表:主键必须包含分区键,否则创建报错(强制要求)

原因:InnoDB 聚簇索引结构,分区依赖主键定位数据,主键必须携带分区键

唯一索引:唯一索引也必须包含分区键,否则无法创建

六、分区表优缺点

优点

大幅提升查询性能,分区裁剪减少扫描数据量

历史数据清理极快,DROP PARTITION 毫秒级,比DELETE高效上万倍

冷热数据分离,热分区常驻内存,冷分区落地磁盘

单表数据量突破单文件大小限制,分散磁盘IO压力

缺点

分区数量不宜过多,建议单表分区控制在100个以内,元数据管理开销变大

跨分区查询、JOIN分区表性能较差,会触发多分区扫描

分区键设计不合理会导致数据倾斜,某个分区过大,失去分区意义

运维成本提升,需要定期规划分区扩容、归档策略

部分MySQL工具对分区表兼容性一般,备份、迁移需要特殊处理

七、生产最佳实践

优先RANGE时间分区:订单、日志、流水90%场景用按月/按天分范围分区

分区键尽量使用原生字段,避免复杂函数,保证分区裁剪生效

主键、唯一索引必须包含分区键,避免建表报错

提前规划分区,不要等到数据溢出MAXVALUE再扩容

冷热分离:冷分区可以单独设置存储引擎、磁盘介质

禁止频繁更新分区键字段,会触发分区数据迁移,产生大量IO

分区数量控制:单表分区50~100个最佳,不要超过200个

备份:分区表建议用物理备份(xtrabackup),逻辑备份会导出全表数据

八、常见坑点

分区键使用函数(如YEAR(create_time)),部分条件下裁剪失效

InnoDB分区表主键必须包含分区键,新手极易踩坑

LIST分区不能插入枚举外的数据,会报错

HASH分区数据不均匀,热点分区性能瓶颈

删除分区不可逆,生产环境务必先备份分区数据

分区表不支持外键约束(InnoDB分区表禁用外键)

相关推荐
陈年老古董1 小时前
Shell 脚本编程基础完整学习笔记
linux·sql·mysql
JavaPub-rodert2 小时前
Redis 和 MySQL 如何保证数据一致性?从缓存更新到延迟双删完整讲解
redis·mysql·缓存
冰暮流星3 小时前
MySQL 流程函数详解
数据库·sql·mysql
烬羽3 小时前
一个博客系统,为什么要拆成 6 张表?
数据库·mysql
程序员-Benothing3 小时前
MySQL 中的日志类型有哪些?binlog、redo log 和 undo log 的作用和区别是什么?
数据库·mysql
2301_800954994 小时前
MySQL 数据库基础:数据类型、查询与备份恢复
数据库·mysql·oracle
起名真的太难了4 小时前
Mysql的数据类型,查询与备份操作
数据库·sql·mysql
程序猿_极客4 小时前
【免费】分享一套优质的基于Php+Mysql的电子购物网站的设计与实现,校园小卖部系统
数据库·mysql·php·购物商城系统