MySQL 分区表是把一张大表按照规则拆分成多个物理小块(分区),逻辑上还是一张完整的表,对上层应用透明,不用改业务SQL。
核心目的:提升查询性能、降低维护成本、方便数据归档清理。
一、分区表基础概念
- 核心原理
逻辑层:一张完整的表,用户操作和普通表无区别
物理层:数据、索引分散存储在多个分区文件中,不同分区独立管理
分区键:用来划分数据的字段/表达式,是分区的核心
- 分区适用场景
表数据量极大(千万/亿级),单表IO压力大
按时间范围归档数据(日志、订单、流水表)
冷热数据分离,频繁查询热数据,冷数据单独存储
按地区、类别拆分数据,缩小查询扫描范围
- 不适合分区的场景
表数据量小,分区反而增加开销
频繁跨分区查询,会触发全分区扫描,性能下降
分区键频繁更新,会导致分区数据迁移,开销巨大
大量随机插入,分区不均匀导致热点分区
二、MySQL支持的分区类型
MySQL 5.1+ 支持4种主流分区,InnoDB、MyISAM 均支持:
- 范围分区 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,否则超出范围数据无法插入
- 列表分区 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 分区不支持多字段枚举,只能单个字段。
- 哈希分区 HASH
根据分区键的哈希值取模,均匀分散数据,避免数据倾斜。
CREATE TABLE goods(
id INT,
stock INT
) ENGINE=InnoDB
PARTITION BY HASH(id)
PARTITIONS 8; -- 分成8个分区
变种:线性哈希分区 LINEAR HASH,采用线性算法,分区扩容/缩容更高效,但数据分散性略差。
- 键分区 KEY
类似哈希分区,MySQL自动对分区键做哈希运算,支持多字段分区。
CREATE TABLE log(
id BIGINT,
user_id INT
) ENGINE=InnoDB
PARTITION BY KEY(id,user_id)
PARTITIONS 6;
- 复合分区(子分区)
先按范围分区,再在每个范围分区内做哈希/列表二次分区,适合超大表。
例:先按时间范围,再按用户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'))
);
三、分区表关键语法操作
- 查看分区信息
-- 查看表分区状态
SELECT PARTITION_NAME,TABLE_ROWS,DATA_LENGTH,INDEX_LENGTH
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_SCHEMA='库名' AND TABLE_NAME='表名';
-- 查看分区定义
SHOW CREATE TABLE 表名;
- 新增分区(范围分区扩容)
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
);
- 删除分区(清理历史数据)
ALTER TABLE order DROP PARTITION p202601;
风险:删除分区会直接删除对应数据,不可逆,InnoDB会立即释放磁盘空间。
- 合并/拆分分区
-- 拆分分区
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);
- 移除分区(变回普通表)
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分区表禁用外键)