根本原因在于分区裁剪失效:当查询条件未使用分区键(如where id in (1,2,3)但分区键是gradeid)时,mysql无法跳过无关分区,被迫全分区扫描,导致i/o和cpu开销倍增。

跨分区查询慢,根本不是“MySQL没优化好”,而是你没让MySQL跳过无关分区。
为什么 WHERE id IN (1,2,3) 在分区表里变慢了
当分区键是 gradeId,而你用主键 id 做 IN 查询时,MySQL 无法做分区裁剪。它得把每个分区都打开一遍,检查里面有没有匹配的 id —— 相当于对 N 个子表分别执行全表扫描(或索引扫描),再合并结果。
常见错误现象:
- 执行
EXPLAIN显示type: ref,但partitions字段列出全部分区(比如p0,p1,...,p127) - 查询耗时随分区数线性增长,哪怕只查 3 个
id - 磁盘 I/O 和 CPU 使用率明显高于非分区表同等查询
分区键和查询条件不匹配 = 白分
分区不是“自动加速器”。它的加速能力完全依赖查询条件是否能命中分区键。只要 WHERE 条件里没出现分区键,或者用了无法推导出具体分区的表达式(比如 WHERE DATE(created_at) = '2025-01-01'),MySQL 就只能扫全分区。
使用场景中容易踩的坑:
- 按
created_atRANGE 分区,却常查user_id→ 扫全分区 - 按
id % 16HASH 分区,却用WHERE id > 1000 AND id → 大概率跨多个分区,且无法精确裁剪 - 分区键是复合字段(如
(tenant_id, created_at)),但只在查询中用了tenant_id→ 某些 MySQL 版本不支持前缀裁剪,仍扫全分区
EXPLAIN PARTITIONS 是唯一靠谱的验证手段
别靠感觉判断有没有裁剪。每次改查询或调分区策略,必须跑这句:
EXPLAIN PARTITIONS SELECT * FROM orders WHERE id IN (1001, 1002);
重点看输出里的 partitions 列:
- 如果显示
NULL或所有分区名 → 没裁剪,危险 - 如果只列 1–2 个分区 → 裁剪有效
- 如果
type是ALL且partitions是全量 → 实际就是 N 次小表全扫
注意:MySQL 8.0+ 对某些函数(如 YEAR()、TO_DAYS())支持分区裁剪,但 DATE()、CAST() 等多数不支持 —— 这点极易被忽略。
跨分区本身不致命,致命的是没控制住扫描量
真正拖垮性能的,从来不是“跨”这个动作,而是随之而来的重复 I/O、内存归并、锁竞争。尤其当单个分区已有百万级数据,又跨了 32 个分区时:
- 即使每个分区只返回 10 行,也要读取 32 ×(索引树深度 × 页面 IO)
- 结果集在 server 层归并排序,内存不够就落磁盘,触发临时表
- 并发查询多时,多个线程争抢同一组 buffer pool 页面,加剧延迟
所以不是“分区错了”,而是你把分区当成了索引替代品 —— 它只负责缩小数据物理范围,不负责加速任意字段的查找。











