分区裁剪需WHERE条件直接匹配分区键,函数包裹如DATE(created_at)会导致失效;正确写法是裸露分区键并用范围谓词,如created_at >= '2024-05-20' AND created_at < '2024-05-21'。

WHERE条件必须直接匹配分区键
分区裁剪不是自动生效的魔法,它依赖优化器能从 WHERE 条件中明确推导出目标分区。最常见失效场景是把分区键套在函数里:WHERE DATE(created_at) = '2024-05-20' 会让 MySQL 或 PostgreSQL 完全放弃裁剪——因为函数掩盖了原始列值,优化器无法判断该去哪个分区。
正确写法是保持分区键裸露并使用支持裁剪的谓词:
WHERE created_at >= '2024-05-20' AND created_at (RANGE 分区推荐)-
WHERE created_at = '2024-05-20'(仅当分区粒度为天且类型精确匹配时可靠) -
WHERE tenant_id IN (101, 102, 105)(LIST/HASH 分区适用)
注意:TO_DAYS(created_at) 这类表达式虽可用于建表,但查询时仍需避免在 WHERE 中对 created_at 做任何转换。
EXPLAIN PARTITIONS 是唯一可信验证手段
别信“我写了日期条件就一定走分区”,必须看执行计划。MySQL 下跑 EXPLAIN PARTITIONS SELECT ...,重点盯 partitions 列:如果显示 p202405,p202406 就成功;若出现 all 或列出几十个分区,说明裁剪失败。
PostgreSQL 则查 EXPLAIN 输出里是否含 Partition Filter 或 Partition Iterator;Oracle 看是否有 PARTITION RANGE SINGLE。没有这些字样,等于没裁剪,聚合还是扫全表。
额外提示:Handler_read_next(有序扫描)远高于 Handler_read_rnd_next(随机回表)通常是裁剪生效的间接信号,但不能替代 EXPLAIN。
GROUP BY 字段和分区键不强绑定,但组合用效果翻倍
分区裁剪只管“扫哪些分区”,GROUP BY 性能还取决于索引和数据局部性。如果按 dt 分区,又常按 region 聚合,建议在每个分区内建复合索引:(dt, region) 或 (region, dt) ——前者利于时间范围+分组,后者利于先分组再限时间。
更关键的是:当 WHERE 已裁剪到 1–3 个分区后,GROUP BY 实际处理的数据量大幅下降,排序/哈希聚合的内存压力和 CPU 开销同步降低。这不是分区本身加速了 GROUP BY,而是它把“大聚合”变成了“几个小聚合”。
陷阱:ORDER BY ... LIMIT 会破坏这个优势。比如 GROUP BY user_id ORDER BY SUM(amount) DESC LIMIT 10,即使裁剪到位,数据库仍需合并所有分区结果再全局排序——此时应考虑物化中间结果或改用窗口函数预过滤。
分区数量与粒度直接影响裁剪收益
按小时分区存一年日志,会产生近 9000 个分区。元数据开销可能压倒 I/O 节省,优化器甚至拒绝裁剪;而按年分区,单个分区动辄上百 GB,裁剪后仍要扫海量数据,意义不大。
实操建议:
- 日志/事件类数据:优先按天 RANGE 分区,单分区控制在 500MB–2GB
- 租户隔离场景:用
HASH(tenant_id)分 64 或 128 个区,避免数据倾斜 - 混合需求(如按月+按租户):确认数据库版本支持二级分区(PostgreSQL 12+、MySQL 8.0+),否则用视图或应用层路由更稳
真正容易被忽略的点是:分区表和非分区表 JOIN 时,若关联字段不是分区键,裁剪大概率失效——这时宁可冗余存储一份分区键,也别让 JOIN 成为裁剪杀手。

















