MySQL分区表仅支持InnoDB,MyISAM虽语法允许但不执行分区逻辑且已废弃;ARCHIVE等引擎直接报错;分区键必须包含在主键或唯一键中;需避免函数导致分区裁剪失效;分区数不宜超过50个。

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 替代多层嵌套函数,能减少分区管理复杂度。
分区不是银弹,它解决的是数据生命周期管理和大范围扫描的物理隔离问题,不是替代索引或分库分表的通用方案。真正卡住性能的,往往在分区之外。










