分区剪枝生效需满足:查询条件必须直接使用分区键且无函数包裹、无隐式转换、符合分区类型限制(如range支持between,list仅支持in);否则将全分区扫描。

分区剪枝没生效?先确认查询条件是否能被优化器识别
MySQL 的分区剪枝(Partition Pruning)不是自动“开箱即用”的魔法,它依赖查询条件中出现的 PARTITION BY 列,且该列必须以**确定性、非函数包裹、非隐式转换**的方式出现在 WHERE 子句中。否则优化器会扫描所有分区,性能和不分区无异。
-
WHERE create_time = '2024-01-01'✅(假设按create_timeRANGE 分区) -
WHERE DATE(create_time) = '2024-01-01'❌(函数包裹,剪枝失效) -
WHERE create_time = STR_TO_DATE('2024-01-01', '%Y-%m-%d')❌(表达式右值不可推导) WHERE create_time >= '2024-01-01' AND create_time ✅(范围可精确映射到分区)
为什么 EXPLAIN 显示 type=ALL 却仍只查了 1 个分区?
EXPLAIN 中的 type=ALL 容易误读为“全表扫描”,其实它只表示“对每个匹配分区做全分区扫描”,不等于扫全部分区。关键看 partitions 列——这才是剪枝是否成功的直接证据。
- 执行
EXPLAIN PARTITIONS SELECT * FROM orders WHERE order_date = '2023-10-15'; - 检查输出中
partitions字段:若显示p202310,说明只访问该分区;若显示p202301,p202302,...,p202312,说明剪枝失败 -
key列为空 ≠ 剪枝失败,分区键本身不依赖二级索引也能剪枝
RANGE 分区 vs LIST 分区:查询写法差异直接影响剪枝能力
RANGE 和 LIST 对查询条件的“可推导性”要求不同。RANGE 更宽容(支持 >、>=、BETWEEN),LIST 更严格(只认明确枚举值或 IN 列表)。
- RANGE 分区(按
id):WHERE id BETWEEN 1000 AND 2000✅ 可剪枝 - LIST 分区(按
region_id):WHERE region_id IN (1,2,5)✅ 可剪枝;WHERE region_id > 3❌ 无法剪枝(优化器不知道哪些分区含 >3 的值) - LIST 分区慎用
NOT IN:MySQL 8.0+ 才支持部分剪枝,5.7 基本失效 - 哈希/KEY 分区只支持
=或IN等价查询,其他一律全扫
ALTER TABLE … REORGANIZE PARTITION 导致慢查询?别在高峰期干这事
分区 DDL 操作(如合并、拆分、重组织)在 MySQL 中是**锁表 + 复制数据**行为,即使只是元数据变更,也可能触发隐式全量重建。线上大表执行时,不仅阻塞写入,还会让后续查询因元数据锁(metadata_lock)卡住。
- 避免在业务高峰执行
REORGANIZE PARTITION或ADD PARTITION - 提前用
SHOW PROCESSLIST观察是否有长时间Waiting for table metadata lock的线程 - 对超大表(>100GB),优先考虑
pt-online-schema-change配合手动分区迁移,而非原地 DDL - 注意:MySQL 8.0 的
ALGORITHM=INSTANT不适用于分区变更,仅限普通列增删
分区剪枝真正起效的前提,是分区键参与查询且写法干净——没有函数、没有类型隐式转换、没有跨分区逻辑判断。很多人调了半天发现没加速,最后发现只是 WHERE FROM_UNIXTIME(ts) 这样一个括号毁所有。









