数据库教程FGMT41‑MySQL性能优化之表分区管理
数据库教程FGMT41‑MySQL性能优化之表分区管理
前言
本套风哥教程面向DBA、数据库运维工程师、云计算运维、后端开发人员,完整讲解MySQL表分区全套知识,兼顾MySQL8.4与MySQL9.7两大版本,两套版本案例各占一半比重。风哥教程本文分为理论原理与实战操作两大模块,理论部分讲解分区概念、优缺点、限制条件、分区与分表差异、各类分区类型原理;实战部分基于主机fgedu‑net‑cn1、fgedu‑net‑cn2,硬件规格统一为64G内存,8CPU,数据根目录统一使用/fgedudb,数据库/实例名fgedudb,业务用户名fgedu,包含大量可直接复现SQL命令,覆盖建分区、新增、删除、重组、交换分区、子分区、分区元数据查看、故障排查。
风哥教程本文学习目标:掌握MySQL分区表设计规范,能够根据业务选择合适分区类型,独立完成分区表创建、日常运维维护,识别分区常见坑,理解分区裁剪原理,具备生产环境分区表落地与故障处理能力。
实操提示:所有SQL优先在测试环境执行,生产环境执行DDL前做好数据备份,评估锁表、元数据锁影响。
网上搜索风哥教程可以学习全套数据库教程
目录
- MySQL表分区基础理论
1.1 什么是MySQL表分区
1.2 不使用分区表带来的业务问题
1.3 使用表分区带来的收益
1.4 MySQL表分区的硬性限制条件
1.5 子分区核心注意事项
1.6 逻辑分区表与物理分表的核心区别
1.7 MySQL8.4与MySQL9.7分区功能差异对比 - MySQL分区类型原理详解
2.1 RANGE范围分区与RANGE COLUMNS范围列分区
2.2 LIST列表分区与LIST COLUMNS列表列分区
2.3 HASH哈希分区、LINEAR HASH线性哈希分区
2.4 KEY分区、LINEAR KEY线性KEY分区
2.5 子分区(复合分区)原理
2.6 分区数据存储位置、DATA DIRECTORY语法说明
2.7 分区裁剪(Partition Pruning)核心原理 - MySQL8.4分区表实战操作案例
3.1 环境准备,数据库与账号初始化
3.2 RANGE、RANGE COLUMNS分区表创建实战
3.3 LIST、LIST COLUMNS分区表创建实战
3.4 HASH、KEY分区表创建实战
3.5 子分区复合分区表创建
3.6 分区元数据信息查看多种方式
3.7 添加分区、删除分区、TRUNCATE清空分区
3.8 REORGANIZE PARTITION重组分区、COALESCE PARTITION合并分区
3.9 重建分区、ANALYZE、OPTIMIZE、CHECK分区维护
3.10 EXCHANGE PARTITION交换分区完整实战
3.11 验证分区裁剪EXPLAIN PARTITIONS实战 - MySQL9.7分区表实战操作案例
4.1 MySQL9.7环境初始化
4.2 RANGE COLUMNS按字符串、日期分区实战
4.3 LIST COLUMNS多列列表分区实战
4.4 9.7版本HASH/KEY分区实战
4.5 9.7子分区复合分区实战
4.6 9.7版本分区各类维护DDL操作
4.7 9.7交换分区实战,跨目录DATA DIRECTORY
4.8 9.7分区裁剪验证、分区统计信息更新 - 分区表生产常见故障与坑点排查
5.1 主键唯一键不包含分区字段导致建表报错
5.2 查询没有触发分区裁剪,扫描全部分区
5.3 RANGE分区新增分区报错MAXVALUE陷阱
5.4 子分区语法错误、子分区不支持HASH再做子分区
5.5 EXCHANGE PARTITION交换分区失败常见原因
5.6 分区表alter table在线DDL锁表风险
5.7 分区数量过多导致性能下降 - 风哥针对本文总结
1 MySQL表分区基础理论
风哥 itpux‑com
1.1 什么是MySQL表分区
MySQL表分区,是把一张逻辑大表,在数据库底层拆分成多个物理子片段,对外仍然呈现为一张完整数据表;上层SQL访问语法完全不变,数据库内核根据分区键表达式,自动把数据路由到对应的物理分区文件,对应用程序透明。
分区属于InnoDB存储引擎层能力,逻辑上是一张表,物理上由多个ibd分区文件组成;应用不需要修改业务SQL,只在建表或者alter table的时候定义分区规则。
逻辑表不变,物理拆分成多个独立分区,DML、DQL语法保持不变,这是分区与手动分表最大区别。
1.2 不使用分区表带来的业务问题
当业务单表数据量持续膨胀,达到千万、亿级数据量级,不做分区会遇到大量现实痛点:
- 历史数据清理成本极高:删除大量历史过期数据,执行delete会产生大量undo、redo日志,锁表,产生大量binlog,IO暴涨,业务卡顿;大批量delete之后还会产生大量碎片,需要optimize table,锁表时间很长。
- 备份恢复效率低:整张大表作为一个ibd文件,备份恢复必须处理全部数据,无法针对部分历史数据做单独备份恢复。
- 查询性能退化:单文件数据量巨大,索引体积膨胀,Buffer Cache缓存压力变大,大量冷数据与热数据混杂,缓存命中率下降。
- 冷热数据无法隔离存储:无法把历史冷数据放到低速存储,热数据放到高速SSD,全部数据只能在同一套存储介质。
- 运维DDL代价巨大:针对部分历史数据的维护操作,必须扫描整张大表,无法隔离操作范围。
网上搜索风哥教程可以学习全套数据库教程
1.3 使用表分区带来的收益
- 历史数据快速清理:使用
DROP PARTITION直接丢弃过期历史分区,几乎不产生redo/undo日志,秒级删除海量历史数据,替代大批量delete删除。 - 分区裁剪提升查询性能:SQL where条件带上分区键,优化器触发分区裁剪,只扫描少数几个分区,跳过大量无关历史分区,减少IO扫描范围。
- 冷热数据物理隔离:通过
DATA DIRECTORY语法,不同分区存放至不同磁盘目录,热数据SSD,冷数据归档低速磁盘,实现存储分层。 - 运维粒度缩小:optimize、analyze、check、truncate可以针对单个分区执行,不需要操作整张巨大逻辑表。
- 交换分区快速数据迁移:
EXCHANGE PARTITION可以把普通表和分区直接交换元数据,实现秒级数据迁入迁出分区,适合数据归档、数据同步场景。 - 备份粒度缩小:可以针对个别分区做独立备份恢复,提升故障恢复效率。
1.4 MySQL表分区的硬性限制条件
- 主键、唯一索引必须全部包含分区字段。只要表存在主键或者任意唯一键,分区表达式里面用到的所有列,必须出现在每一个主键、唯一键字段集合里面。根源:MySQL分区引擎无法跨分区做唯一性校验,如果唯一键不包含分区字段,不同分区可以出现相同唯一值,破坏唯一性约束。
- InnoDB引擎才完整支持分区,MyISAM虽然语法支持,但生产不推荐;NDB集群引擎有额外分区约束。
- 不支持FULLTEXT全文索引、SPATIAL空间索引,分区表上面不能创建全文索引、空间索引。
- 临时表不能做分区。
- 分区表达式允许函数有限,仅支持官方允许的时间、数学函数,不支持存储过程、自定义函数、子查询作为分区表达式。
- 单张分区表最大分区数量上限8192,分区不是越多越好,分区数量过多,元数据解析开销会上升。
- TEXT、BLOB不能直接作为分区键;KEY分区类型例外。
风哥教程 113257174
1.5 子分区核心注意事项
子分区也就是复合分区,外层是一级分区,每一个一级分区再拆分成二级子分区。
硬性规则:
- 只有RANGE、LIST类型可以做一级父分区;HASH、KEY不能作为父分区,不能再对子分区继续做子分区。
- 子分区只允许使用HASH / KEY类型,RANGE/LIST不能充当子分区。
- 全部一级分区,子分区数量必须完全保持一致,不能部分分区2个子分区,另外分区4个子分区。
- 主键、唯一键约束同样需要包含父分区字段+子分区字段。
1.6 分表和分区有什么区别
| 对比维度 | MySQL分区表 | 业务手动分表(分表) |
|---|---|---|
| 上层SQL | 对外一张逻辑表,业务SQL不用修改 | 多张物理表,业务层需要做路由,拼接表名、union all |
| 数据库内核 | MySQL内核实现,对应用透明 | 完全业务代码中间件层实现,数据库无感知 |
| DDL运维 | alter table维护分区,数据库原生语法 | 需要业务层管理多张表DDL,同步每张表结构 |
| 唯一性约束 | 数据库层面支持主键唯一键(满足分区键约束) | 跨分表数据库无法校验唯一,业务层自行保证 |
| 子查询、join | 直接正常写SQL | 业务层处理多表union,join逻辑复杂 |
| 故障风险 | 数据库层实现,风险集中在数据库 | 风险集中业务代码/中间件,代码出错容易产生数据错乱 |
1.7 MySQL8.4与MySQL9.7分区功能差异对比
上51CTO搜索风哥可以学习全套数据库教程
| 功能点 | MySQL8.4 | MySQL9.7 |
|---|---|---|
| RANGE COLUMNS / LIST COLUMNS | 完整支持,支持多列、字符串、日期直接做分区键 | 完整支持,语法兼容,元数据优化 |
| 子分区 | RANGE/LIST下嵌套HASH/KEY | 语法完全兼容,修复部分子分区元数据BUG |
| EXCHANGE PARTITION | 普通InnoDB表交换分区,要求表结构、索引完全一致 | 增强,对大对象LOB字段校验逻辑完善 |
| 分区DDL算法ALGORITHM | 支持inplace算法,部分分区DDL支持在线 | 更多alter partition操作支持ALGORITHM=INPLACE,减少锁元数据时间 |
| 分区元数据information_schema.partitions | 基础元数据 | 增加更多统计字段,子分区统计信息更完善 |
| DATA DIRECTORY跨目录分区 | 支持,每个分区指定独立目录 | 支持,权限校验更加严格 |
| 限制条件 | 主键唯一键必须包含分区键 | 继承8.4全部约束,部分报错信息更加清晰友好 |
2 MySQL分区类型原理详解
2.1 RANGE范围分区与RANGE COLUMNS范围列分区
RANGE分区:根据表达式返回的整数值范围划分分区,VALUES LESS THAN()定义每个分区上限,适合时间、自增ID这类连续有序字段,最常用于按月份、按年份归档历史数据。
注意RANGE分区的值域不能重叠,分区范围连续递增;如果新增数据超过全部分区定义范围,直接报错,一般会增加MAXVALUE兜底分区。
RANGE COLUMNS:是RANGE的增强版本,不需要写函数表达式,可以直接使用列本身,支持多列、日期、字符串,不需要转换成整数,8.4/9.7强烈优先推荐RANGE COLUMNS,规避函数表达式带来的分区裁剪失效风险。
2.2 LIST列表分区与LIST COLUMNS列表列分区
LIST分区,每一个分区对应离散的枚举值集合,VALUES IN (val1,val2),适合地域、业务类型、状态码这类离散有限枚举字段。
LIST COLUMNS增强:支持多列、字符串、日期,不需要表达式,多列元组匹配,不需要必须整数。
LIST分区如果插入不在任何list枚举集合内的值,直接报错,没有MAXVALUE兜底机制。
2.3 HASH哈希分区、LINEAR HASH线性哈希分区
HASH分区,根据分区键表达式哈希取模,均匀打散数据分布到各个分区,不关心数据范围,目标把数据均匀分散。
LINEAR HASH使用线性哈希算法,新增删除分区代价更低,但是数据分布均匀度会略差;普通HASH重分区会全部重新哈希数据。
2.4 KEY分区、LINEAR KEY线性KEY分区
KEY分区类似HASH,但是哈希算法由MySQL内部提供,可以直接使用非整数列,不需要写表达式;如果不指定列,默认使用主键列。
LINEAR KEY对应线性版本。
风哥数据库教程 itpux‑com
2.5 子分区(复合分区)原理
子分区,父分区RANGE/LIST,每个父分区下面再拆分成HASH或者KEY子分区。
例如按年份RANGE做一级分区,每一年的数据再按user_id HASH拆分成多个子分区。
物理存储:每一个子分区对应独立ibd文件。
注意:所有父分区子分区数量必须完全相等。
2.6 分区存储位置 DATA DIRECTORY语法说明
InnoDB分区表,默认所有分区ibd文件统一放在实例datadir数据库目录;
使用DATA DIRECTORY='/fgedudb/partition_archive',可以为单个分区指定独立存储目录,实现冷热数据分磁盘存放。
注意:目录必须mysql用户可读可写,需要开启innodb_file_per_table,不能用于共享表空间ibdata1。
2.7 分区裁剪(Partition Pruning)核心原理
分区裁剪是分区表性能收益的核心。
当SQL查询where条件带上分区键过滤条件,MySQL优化器解析条件,计算出只需要访问哪几个分区,直接跳过其余全部分区文件,减少IO读取。
失效场景:
- where条件分区键包裹函数运算,例如
where YEAR(create_time)='2026',分区键字段上做函数运算,优化器无法推导分区;推荐直接字段做范围比较。 - where条件不带任何分区键过滤条件,必须扫描全部分区。
- join关联条件,无法下推分区键过滤。
使用EXPLAIN PARTITIONS SELECT ...可以查看执行计划的partitions字段,确认实际扫描哪些分区,验证裁剪是否生效。
3 MySQL8.4分区表实战操作案例
实验主机
fgedu‑net‑cn1,MySQL8.4,硬件规格64G内存8CPU;实例数据目录/fgedudb/fgedudb,数据库fgedudb,账号fgedu。
登录数据库:
mysql -S /fgedudb/fgedudb/mysql.sock
3.1 环境准备,数据库与账号初始化
CREATE DATABASE IF NOT EXISTS fgedudb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE fgedudb;
CREATE USER IF NOT EXISTS 'fgedu'@'%' IDENTIFIED BY 'Fg@123456';
GRANT ALL PRIVILEGES ON fgedudb.* TO 'fgedu'@'%';
FLUSH PRIVILEGES;
3.2 RANGE、RANGE COLUMNS分区表创建实战
RANGE分区(按年份函数)
CREATE TABLE `operate_log_range` (
id BIGINT NOT NULL AUTO_INCREMENT,
operate_user VARCHAR(64),
operate_content TEXT,
create_time DATETIME NOT NULL,
PRIMARY KEY(id,YEAR(create_time))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE (YEAR(create_time)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
注意主键必须带上分区表达式字段YEAR(create_time),否则8.4会报建表失败。
RANGE COLUMNS 推荐用法,直接使用datetime列
CREATE TABLE `operate_log_rc` (
id BIGINT NOT NULL AUTO_INCREMENT,
operate_user VARCHAR(64),
operate_content TEXT,
create_time DATETIME NOT NULL,
PRIMARY KEY(id,create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE COLUMNS(create_time) (
PARTITION p2023 VALUES LESS THAN ('2024-01-01 00:00:00'),
PARTITION p2024 VALUES LESS THAN ('2025-01-01 00:00:00'),
PARTITION p2025 VALUES LESS THAN ('2026-01-01 00:00:00'),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
插入测试数据:
INSERT INTO operate_log_rc(operate_user,operate_content,create_time) VALUES
('user01','login','2023‑05‑10 10:20:00'),
('user02','modify','2024‑08‑11 14:30:00'),
('user03','logout','2025‑02‑03 09:10:00');
网上搜索风哥教程可以学习全套数据库教程
3.3 LIST、LIST COLUMNS分区表创建实战
LIST分区,按业务状态枚举:
CREATE TABLE `biz_order_list` (
order_id BIGINT NOT NULL AUTO_INCREMENT,
user_name VARCHAR(32),
order_status TINYINT NOT NULL COMMENT '1待支付 2已支付 3已取消 4已退款',
amount DECIMAL(18,2),
PRIMARY KEY(order_id,order_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY LIST(order_status)(
PARTITION p_pay_wait VALUES IN (1),
PARTITION p_payed VALUES IN (2),
PARTITION p_cancel VALUES IN (3,4)
);
LIST COLUMNS多列字符串示例:
CREATE TABLE `biz_order_lc` (
order_id BIGINT NOT NULL AUTO_INCREMENT,
region_code VARCHAR(16) NOT NULL,
order_status VARCHAR(16) NOT NULL,
amount DECIMAL(18,2),
PRIMARY KEY(order_id,region_code,order_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY LIST COLUMNS(region_code,order_status)(
PARTITION p_shenzhen VALUES IN (('SZ','PAY'),('SZ','CANCEL')),
PARTITION p_beijing VALUES IN (('BJ','PAY'),('BJ','CANCEL'))
);
3.4 HASH、KEY分区表创建实战
普通HASH分区:
CREATE TABLE `user_hash` (
uid BIGINT NOT NULL AUTO_INCREMENT,
nickname VARCHAR(64),
phone VARCHAR(20),
PRIMARY KEY(uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY HASH(uid)
PARTITIONS 4;
LINEAR HASH线性HASH:
CREATE TABLE `user_lhash` (
uid BIGINT NOT NULL AUTO_INCREMENT,
nickname VARCHAR(64),
PRIMARY KEY(uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY LINEAR HASH(uid)
PARTITIONS 4;
KEY分区,不指定列默认主键:
CREATE TABLE `user_key` (
uid BIGINT NOT NULL AUTO_INCREMENT,
nickname VARCHAR(64),
PRIMARY KEY(uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY KEY()
PARTITIONS 4;
3.5 子分区复合分区表创建
RANGE作为父分区,子分区HASH:
CREATE TABLE `log_subpart` (
id BIGINT NOT NULL AUTO_INCREMENT,
user_id BIGINT NOT NULL,
msg TEXT,
create_time DATETIME NOT NULL,
PRIMARY KEY(id,create_time,user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE COLUMNS(create_time)
SUBPARTITION BY HASH(user_id) SUBPARTITIONS 2
(
PARTITION p2024 VALUES LESS THAN ('2025‑01‑01'),
PARTITION p2025 VALUES LESS THAN ('2026‑01‑01'),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
3.6 分区元数据信息查看多种方式
查询information_schema.partitions系统表,最常用:
SELECT
PARTITION_NAME,SUBPARTITION_NAME,TABLE_ROWS,PARTITION_EXPRESSION,SUBPARTITION_EXPRESSION
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA='fgedudb' AND TABLE_NAME='operate_log_rc';
show create table查看完整分区定义:
SHOW CREATE TABLE operate_log_rc;
验证分区裁剪,EXPLAIN PARTITIONS:
EXPLAIN PARTITIONS SELECT * FROM operate_log_rc WHERE create_time>='2024‑01‑01' AND create_time <'2025‑01‑01';
3.7 添加分区、删除分区、TRUNCATE清空分区
⚠️DROP PARTITION会直接删除分区内全部数据,谨慎操作。
给RANGE COLUMNS表新增分区:
ALTER TABLE operate_log_rc ADD PARTITION (
PARTITION p2026 VALUES LESS THAN ('2027‑01‑01 00:00:00')
);
删除分区:
ALTER TABLE operate_log_rc DROP PARTITION p2023;
清空分区内全部数据,保留分区定义:
ALTER TABLE operate_log_rc TRUNCATE PARTITION p2024;
3.8 REORGANIZE PARTITION重组分区、COALESCE PARTITION合并分区
REORGANIZE PARTITION:拆分RANGE/LIST分区,把一个大分区拆成多个小分区,数据自动迁移。
ALTER TABLE operate_log_rc REORGANIZE PARTITION p_max INTO (
PARTITION p2027 VALUES LESS THAN ('2028‑01‑01'),
PARTITION p_future_max VALUES LESS THAN MAXVALUE
);
COALESCE PARTITION:用于HASH/KEY分区,减少分区数量。例如4个hash分区缩减成2个:
ALTER TABLE user_hash COALESCE PARTITION 2;
3.9 重建分区、ANALYZE、OPTIMIZE、CHECK分区维护
更新分区统计信息,优化器依靠统计信息生成执行计划:
ANALYZE TABLE operate_log_rc PARTITION (p2024,p2025);
检查分区数据文件完整性:
CHECK TABLE operate_log_rc PARTITION (p2024);
OPTIMIZE,整理分区碎片:
OPTIMIZE TABLE operate_log_rc PARTITION (p2024);
3.10 EXCHANGE PARTITION交换分区完整实战
交换分区:把普通InnoDB表和分区表的一个分区交换元数据,几乎秒级,两张表结构、字段、索引、主键必须完全一致;数据不会拷贝,只是元数据交换。
1、准备普通表:
CREATE TABLE tmp_operate_2026 LIKE operate_log_rc;
-- 向临时表灌入数据
INSERT INTO tmp_operate_2026(operate_user,operate_content,create_time)
VALUES('u05','test','2026‑03‑01 11:00:00');
2、执行交换,把tmp_operate_2026数据交换进入p2026分区:
ALTER TABLE operate_log_rc EXCHANGE PARTITION p2026 WITH TABLE tmp_operate_2026;
交换完成后,原来临时表变为空,分区p2026拥有这批数据。
风哥教程 113257174
4 MySQL9.7分区表实战操作案例
实验主机
fgedu‑net‑cn2,MySQL9.7,硬件规格64G内存8CPU;实例数据目录/fgedudb/fgedudb,数据库fgedudb,账号fgedu。
MySQL9.7分区语法大体兼容8.4,报错提示更加完善,部分DDL支持更多inplace算法。
登录数据库:
mysql -S /fgedudb/fgedudb/mysql.sock
4.1 MySQL9.7环境初始化
CREATE DATABASE IF NOT EXISTS fgedudb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE fgedudb;
CREATE USER IF NOT EXISTS 'fgedu'@'%' IDENTIFIED BY 'Fg@123456';
GRANT ALL PRIVILEGES ON fgedudb.* TO 'fgedu'@'%';
FLUSH PRIVILEGES;
4.2 RANGE COLUMNS按字符串、日期分区实战
9.7 RANGE COLUMNS支持字符串、datetime直接做分区键,不需要函数。
CREATE TABLE `order_97_rc` (
order_id BIGINT NOT NULL AUTO_INCREMENT,
order_no VARCHAR(48),
create_dt DATETIME NOT NULL,
city_code VARCHAR(24),
amount DECIMAL(18,2),
PRIMARY KEY(order_id,create_dt,city_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE COLUMNS(create_dt,city_code)(
PARTITION p2024_sz VALUES LESS THAN ('2025‑01‑01','SZ'),
PARTITION p2024_bj VALUES LESS THAN ('2025‑01‑01','ZZ'),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
INSERT INTO order_97_rc(order_no,create_dt,city_code,amount) VALUES
('ORD97001','2024‑06‑01 08:30:00','SZ',120.50),
('ORD97002','2024‑07‑02 09:20:00','BJ',88.00);
4.3 LIST COLUMNS多列列表分区实战
CREATE TABLE `biz_97_lc` (
id BIGINT NOT NULL AUTO_INCREMENT,
prov_code VARCHAR(16),
pay_type VARCHAR(16),
cnt INT,
PRIMARY KEY(id,prov_code,pay_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY LIST COLUMNS(prov_code,pay_type)(
PARTITION p_gd VALUES IN (('GD','ALI'),('GD','WX')),
PARTITION p_js VALUES IN (('JS','ALI'),('JS','WX'))
);
4.4 9.7版本HASH/KEY分区实战
CREATE TABLE `user_97_hash` (
uid BIGINT NOT NULL AUTO_INCREMENT,
uname VARCHAR(64),
PRIMARY KEY(uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY HASH(uid)
PARTITIONS 4;
4.5 9.7子分区复合分区实战
RANGE COLUMNS父分区,子分区KEY:
CREATE TABLE `log_97_sub` (
id BIGINT NOT NULL AUTO_INCREMENT,
uid BIGINT NOT NULL,
msg_content TEXT,
ct DATETIME NOT NULL,
PRIMARY KEY(id,ct,uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE COLUMNS(ct)
SUBPARTITION BY KEY(uid) SUBPARTITIONS 2
(
PARTITION p2024 VALUES LESS THAN ('2025‑01‑01'),
PARTITION p2025 VALUES LESS THAN ('2026‑01‑01'),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
4.6 9.7版本分区各类维护DDL操作
新增分区:
ALTER TABLE order_97_rc ADD PARTITION (
PARTITION p2025_sz VALUES LESS THAN ('2026‑01‑01','SZ')
);
重组分区:
ALTER TABLE order_97_rc REORGANIZE PARTITION p_future INTO (
PARTITION p2025_rest VALUES LESS THAN ('2026‑01‑01','ZZ'),
PARTITION p_97_max VALUES LESS THAN MAXVALUE
);
删除分区:
ALTER TABLE order_97_rc DROP PARTITION p2024_sz;
truncate清空分区:
ALTER TABLE order_97_rc TRUNCATE PARTITION p2024_bj;
hash分区合并:
ALTER TABLE user_97_hash COALESCE PARTITION 2;
统计信息、完整性检查:
ANALYZE TABLE order_97_rc PARTITION(p2024_bj);
CHECK TABLE order_97_rc PARTITION(p2024_bj);
4.7 9.7交换分区实战,跨目录DATA DIRECTORY
普通临时表:
CREATE TABLE tmp_97_order LIKE order_97_rc;
INSERT INTO tmp_97_order(order_no,create_dt,city_code,amount)
VALUES('ORD97099','2025‑04‑05 10:10:00','SZ',233.00);
执行交换分区:
ALTER TABLE order_97_rc EXCHANGE PARTITION p2025_sz WITH TABLE tmp_97_order;
创建带独立存储目录分区示例,9.7对目录权限校验严格,目录提前创建,mysql属主:
mkdir -p /fgedudb/part_archive_97
chown mysql:mysql /fgedudb/part_archive_97
ALTER TABLE order_97_rc ADD PARTITION (
PARTITION p_archive_97 VALUES LESS THAN ('2028‑01‑01','ZZ')
DATA DIRECTORY='/fgedudb/part_archive_97'
);
风哥数据库教程 itpux‑com
4.8 9.7分区裁剪验证、分区统计信息更新
EXPLAIN PARTITIONS SELECT * FROM order_97_rc WHERE create_dt>='2024‑01‑01' AND create_dt <'2025‑01‑01';
SELECT PARTITION_NAME,TABLE_ROWS FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA='fgedudb' AND TABLE_NAME='order_97_rc';
5 分区表生产常见故障与坑点排查
5.1 主键唯一键不包含分区字段导致建表报错
现象:创建分区表报 A PRIMARY KEY must include all columns in the table's partitioning function。
根因:主键、唯一索引里面没有包含分区表达式全部字段,MySQL分区引擎无法跨分区校验唯一性。
解决方案:修改主键/唯一键,把分区字段加入主键;业务允许情况下删除唯一约束。
5.2 查询没有触发分区裁剪,扫描全部分区
排查步骤:
- 使用
EXPLAIN PARTITIONS查看partitions字段,如果列出全部分区,代表裁剪失效。 - 检查where条件,分区字段外面是否包裹函数运算,例如
WHERE YEAR(create_time)='2025',改为直接字段范围比较create_time >= '2025‑01‑01' AND create_time < '2026‑01‑01'。 - 确认SQL确实携带分区键过滤条件;完全不带分区键条件,必然扫描全部分区。
- 确认统计信息准确,执行
ANALYZE TABLE更新分区统计。
5.3 RANGE分区新增分区报错MAXVALUE陷阱
现象:已经定义PARTITION p_max VALUES LESS THAN MAXVALUE,此时直接ADD PARTITION会报错。
原因:MAXVALUE分区已经接纳全部值域,不能直接add;需要使用REORGANIZE PARTITION拆分MAXVALUE分区,拆分出新分区,保留新的MAXVALUE兜底分区。
5.4 子分区语法错误、子分区不支持HASH再做子分区
坑1:HASH、KEY不能作为一级父分区,不能对子分区继续嵌套子分区;只有RANGE、LIST允许作为父分区。
坑2:各个父分区子分区数量必须全部相同,不能有的2个子分区、有的4个子分区。
5.5 EXCHANGE PARTITION交换分区失败常见原因
- 两张表结构不一致:字段、字段类型、索引、主键、字符集、排序规则不一致。
- 临时表里面存在数据,但是分区定义不允许容纳这批数据,数据值不在分区值域范围内。
- 包含FULLTEXT、SPATIAL索引,分区表不支持。
- 分区表使用DATA DIRECTORY跨目录,临时表没有对应配置。
5.6 分区表alter table在线DDL锁表风险
ALTER TABLE ... PARTITION BY ... 把普通大表直接改成分区表,8.4/9.7会重建整张表,会锁表,数据量大生产严禁直接执行。
生产落地建议:新建空分区表,业务双写,数据迁移完成之后rename切换表名;或者在备库完成改造,主备切换上线,避免线上锁表。
5.7 分区数量过多导致性能下降
单张表分区数量不要无限制创建,上限8192;分区数量几百上千,每次SQL解析、元数据加载开销会显著上升。
生产建议,按业务周期规划,按月/按年分区,不要按天产生几千个分区。
上51CTO搜索风哥可以学习全套数据库教程
6 风哥针对本文总结
风哥教程本文完整讲解MySQL表分区整套知识,兼顾MySQL8.4、MySQL9.7两个版本理论与完整实操,硬件环境统一64G内存8CPU,数据目录/fgedudb,数据库fgedudb。
核心要点梳理:
- MySQL分区表逻辑一张表,底层拆分成多个物理分区文件,业务SQL基本不用修改;区分分区表和业务层手动分表,分区是数据库内核原生能力,手动分表属于业务/中间件层实现。
- 分区类型分为RANGE、RANGE COLUMNS、LIST、LIST COLUMNS、HASH、KEY;优先推荐COLUMNS系列分区,直接使用列,不写函数表达式,大幅降低分区裁剪失效风险。子分区父分区只能是RANGE/LIST,子分区只能HASH/KEY。
- 硬性约束:主键、所有唯一键必须包含分区表达式全部字段;分区表不能使用全文索引、空间索引;临时表不能分区。
- 分区最大价值来自两点:①DROP PARTITION秒级清理海量历史过期数据,替代大批量delete;②分区裁剪,where条件携带分区键,扫描少量分区,减少IO。分区不是银弹,不是大表就必须分区,如果查询永远不带分区键条件,分区裁剪完全无法生效,分区不会带来性能收益。
- 运维常用DDL:ADD新增分区、DROP删除分区(删除数据)、TRUNCATE PARTITION清空分区保留定义、REORGANIZE拆分分区、COALESCE合并HASH/KEY分区、EXCHANGE PARTITION交换分区元数据实现秒级迁移数据。使用
EXPLAIN PARTITIONS验证分区裁剪效果,查询information_schema.PARTITIONS查看分区元数据。 - 生产落地注意:不要直接alter table把千万级大表原地转换成分区表,会重建全表,锁表;推荐新建分区表,双写迁移,rename切换。RANGE分区存在MAXVALUE陷阱,有MAXVALUE不能直接ADD PARTITION,要用REORGANIZE拆分。控制分区数量,不要无限制疯狂新建分区,避免元数据性能退化。
- MySQL8.4和MySQL9.7分区语法大部分兼容,9.7主要增强报错提示、部分DDL支持更多INPLACE在线算法,子分区元数据统计信息更加完善。
- 生产上线分区表,前期做好测试:验证分区裁剪、模拟过期分区删除、模拟交换分区迁移,业务SQL全部做兼容性验证,上线之后定期维护分区,提前新增未来分区,避免新数据插入直接报错。
分区是运维优化手段,需要匹配业务访问模式,如果业务SQL绝大多数查询不带分区过滤条件,分区无法发挥收益,就不建议使用分区表。
- 点赞
- 收藏
- 关注作者
评论(0)