首页/数据/SQL 速查/分区裁剪:让查询只扫目标分区
性能与执行计划72 条

分区裁剪:让查询只扫目标分区

按分区键的常量条件定位分区:EXPLAIN 中会显示裁剪结果,分区键上的函数往往阻止裁剪。

SQL 速查数据更新于 2026-10-13

分区裁剪是什么

分区裁剪(partition pruning)指查询条件能直接定位到部分分区,执行时只扫这些分区、跳过其余分区。生效前提是过滤条件作用在分区键上,且是常量或能在计划期求值的表达式。它不改变表的语义,只是让扫描范围变小;判断是否生效最快的办法就是看执行计划里列出了哪些分区。

基本信息

适用对象MySQL 8.0 分区表;PostgreSQL 声明式分区表
生效前提过滤条件落在分区键上,且为常量或可在计划期求值
MySQL 查看EXPLAIN 的 partitions 列,JSON 计划中同样有
PostgreSQL 查看EXPLAIN 中 Append 节点下列出的子分区
主要收益扫描量随命中分区数下降,归档与清理可按分区整块做

典型使用场景

  • 时间分区表按月查询:范围条件只命中一两个月分区
  • 历史数据归档:直接对旧分区分块删除或转储,绕开大范围 DELETE
  • 分区级维护:只对单个分区重建索引或更新统计,影响面小
  • 排查全分区扫描:确认计划里列出的是否只有目标分区
  • 改写被打断的条件:把分区键外层的函数调用换成范围条件以恢复裁剪
  • 冷热数据分层:热分区常驻内存,冷分区放到慢速存储

MySQL 验证裁剪

MySQL 验证裁剪
EXPLAIN SELECT * FROM logs
WHERE dt >= '2024-01-01' AND dt < '2024-02-01';   # partitions 列应只列出 2024 年 1 月对应分区
EXPLAIN SELECT * FROM logs WHERE YEAR(dt) = 2024;  # 分区键套函数,partitions 列会列出全部分区
SELECT partition_name, table_rows, data_length     # 查看各分区的行数与占用空间
FROM information_schema.partitions
WHERE table_schema = DATABASE() AND table_name = 'logs';
ALTER TABLE logs DROP PARTITION p2023;             # 整分区清理历史数据,代价远低于 DELETE

PostgreSQL 验证裁剪

PostgreSQL 验证裁剪
EXPLAIN (ANALYZE) SELECT * FROM logs
WHERE dt >= DATE '2024-01-01' AND dt < DATE '2024-02-01';  # Append 下只列存活分区
SHOW enable_partition_pruning;                    # 确认裁剪开关处于打开状态
EXPLAIN SELECT * FROM logs WHERE date_trunc('year', dt) = DATE '2024-01-01';
-- 对分区键套函数后通常只能全分区扫描,改成范围条件才能裁
SELECT relname, n_live_tup FROM pg_stat_user_tables  # 逐个分区看行数分布
WHERE relname LIKE 'logs_%' ORDER BY relname;
ALTER TABLE logs DETACH PARTITION logs_2023;      # 分离旧分区后再归档或删除

裁剪信号逐项读法

信号含义怎么判断生效
partitions 列(MySQL)本次执行预计访问的分区名单列出全部分区说明裁剪没生效
type 与 rows(MySQL)访问方式与估算行数裁剪生效后 rows 应明显下降
Append 子节点(PostgreSQL)各分区的扫描分支分支数与命中分区数一致才算裁到位
Subplans Removed(PostgreSQL)运行期被裁掉的分支数量大于 0 说明运行期裁剪起了作用
constraint exclusion早期版本用于裁剪的开关机制关掉后退化为全分区扫描
分区键上做函数运算或隐式类型转换都会挡住裁剪:把 YEAR(dt) = 2024 改写成 dt >= '2024-01-01' AND dt < '2025-01-01',把字符串与日期列的裸比较改成显式类型转换,裁剪才能重新生效。
MySQL 分区表自带限制:分区表不支持外键,且每个唯一索引都必须包含分区表达式里的全部列。分区键要在建表前连同这些约束一起设计,否则会在建表阶段直接失败。
分区数不是越多越好:分区数量膨胀会放大打开文件与元数据的开销,小表分区几乎没有收益,粒度应按数据量与归档周期来定。

易错点

  • 对分区键套函数或隐式转换:裁剪失效退化为全分区扫描,需改写成范围条件
  • 分区数量设计过多:元数据与文件句柄开销上升,收益可能为负
  • 把分区当成索引:分区只缩小候选范围,分区内部的过滤依然要靠索引或逐行判断
  • 查询条件不含分区键:这类语句必然扫描所有分区,大表上代价很高
  • MySQL 唯一索引没包含分区列:建表直接报错,必须在设计阶段就确认

数据来源与参考

  • MySQL 8.0 Reference Manual · Partition Pruning
  • PostgreSQL Documentation · Partition Pruning
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌