分区裁剪是什么
分区裁剪(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; # 整分区清理历史数据,代价远低于 DELETEPostgreSQL 验证裁剪
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 分区表自带限制:分区表不支持外键,且每个唯一索引都必须包含分区表达式里的全部列。分区键要在建表前连同这些约束一起设计,否则会在建表阶段直接失败。
分区数不是越多越好:分区数量膨胀会放大打开文件与元数据的开销,小表分区几乎没有收益,粒度应按数据量与归档周期来定。