MySQL 分区表只支持 InnoDB 和 MyISAM,但 MyISAM 已被弃用
MySQL 8.0 起,
CREATE TABLE ... PARTITION BY语句仅允许在
InnoDB存储引擎上创建分区表;
MyISAM虽语法上仍接受分区定义,但实际不执行分区逻辑(写入全落一个分区),且官方已标记为废弃。使用
ARCHIVE、
MEMORY、
CSV等引擎尝试建分区表会直接报错:
ERROR 1031 (HY000): Table storage engine for 't' doesn't support partitioning。
实操建议:
新项目必须用InnoDB+ 分区,不要碰
MyISAM分区 升级老系统前检查
SHOW CREATE TABLE t是否含
ENGINE=MyISAM和
PARTITION BY,这类表需先转引擎再验证分区行为
InnoDB分区本质是多个独立的
.ibd文件(每个分区一个),不是逻辑切分,所以
innodb_file_per_table=ON必须启用
分区键必须是主键/唯一键的全部组成部分
这是最常踩的坑:定义
PARTITION BY RANGE/LIST/HASH时,分区表达式里涉及的列,必须包含在表的每个唯一约束(含主键)中。否则建表失败:
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function。
比如这张表会失败:
CREATE TABLE logs ( id BIGINT PRIMARY KEY, dt DATE, msg TEXT ) ENGINE=InnoDB PARTITION BY RANGE (YEAR(dt)) ( PARTITION p2023 VALUES LESS THAN (2024), PARTITION p2024 VALUES LESS THAN (2025) );
因为
id是主键,但分区函数只用了
dt,没包含
id。修复方式只有两种: 把
dt加进主键:
PRIMARY KEY (id, dt)(注意顺序,
dt放后面不影响查询效率) 改用
KEY(dt)或
HASH(YEAR(dt)),但前提是主键本身含
dt或声明
UNIQUE KEY(dt)
没有“绕过”办法——这是 InnoDB 分区的硬性索引一致性要求。
分区裁剪失效的典型场景和验证方法
分区的价值全靠查询时的
partition pruning(分区裁剪)。但很多看似能裁剪的 WHERE 条件,实际无法触发裁剪。常见失效点: 对分区字段用了函数:
WHERE YEAR(dt) = 2024→ 不裁剪;必须写成
WHERE dt >= '2024-01-01' AND dt分区字段参与了计算:
WHERE dt + INTERVAL 1 DAY > '2024-01-01'→ 不裁剪 使用了非确定性函数:
WHERE dt > NOW()→ 优化器无法预判分区范围 JOIN 中分区表作为被驱动表,且 ON 条件未覆盖分区键 → 可能全分区扫描
验证是否裁剪:执行
EXPLAIN PARTITIONS SELECT ...,看
partitions列是否只列出目标分区(如
p2024),而不是
p2023,p2024,p2025或
NULL。
分区数量不是越多越好,50 个以上要警惕
InnoDB 对单表分区数没有硬上限,但超多分区会显著拖慢 DDL 和查询优化阶段:
ALTER TABLE ... REORGANIZE PARTITION会锁整个表,分区越多,元数据操作越慢 优化器估算执行计划时需遍历所有分区的统计信息,分区数 > 50 时
EXPLAIN延迟明显上升 每个分区对应独立的
.ibd文件,海量小分区易触发文件系统 inode 耗尽或 open file limit 问题 备份工具(如
mysqldump --tab或物理备份)需逐个处理分区文件,恢复时间线性增长
真实建议:按月分区最多支撑 3–5 年历史数据(36–60 个分区);按天分区慎用,除非单日数据量稳定在 GB 级且查询极度聚焦最近 N 天;用
RANGE COLUMNS替代多层嵌套函数,能减少分区管理复杂度。
分区不是银弹,它解决的是数据生命周期管理和大范围扫描的物理隔离问题,不是替代索引或分库分表的通用方案。真正卡住性能的,往往在分区之外。
