PostgreSQL 动态分区裁剪技术:从原理到实战的查询性能优化
当数据量从 TB 迈向 PB,数据库查询性能往往成为瓶颈。PostgreSQL 凭借其高度可扩展性,在金融、电商等领域广泛应用。但在海量数据场景下,如何通过分区裁剪精准定位目标数据,避免无关分区的无效扫描,是提升查询效率的关键。
我们将从源码层面剖析其机制,并结合实战案例验证效果,重点探讨不同场景下的优化策略。
一、核心原理与工作流程
1.1 静态与动态裁剪的区别
PostgreSQL 的分区裁剪主要分为两种模式。根据官方源码 partprune.c 的逻辑,系统会将查询条件转换为"pruning steps",在执行时识别需要扫描的分区集合。
静态裁剪依赖于编译时已知的常量。优化器在计划生成阶段就能排除无关分区。例如按日期范围分区,查询 WHERE order_date = '2026-01-01' 时,直接锁定对应分区。
动态裁剪则支持运行时过滤。当条件涉及参数(如 $1)或子查询时,PostgreSQL 会在执行阶段根据实际值判断分区。这在处理参数化查询时尤为重要。
PolarDB 文档进一步将剪枝分为三个层级,这对理解执行时机很有帮助:
- 优化期剪枝:适用于不可变表达式(如常量),在计划生成阶段完成。
- 执行期初始剪枝:适用于稳定表达式(如
now()),在初始化阶段完成。 - 执行期运行时剪枝:适用于易变表达式或子查询,在执行过程中动态完成。
1.2 源码视角的实现
核心逻辑集中在 partprune.c。关键数据结构包括匹配分区键的子句信息 (PartClauseInfo) 和生成剪枝步骤的上下文 (GeneratePruningStepsContext)。
/* 匹配分区键的子句信息 */
typedef struct PartClauseInfo {
int keyno; /* 分区键索引 */
Oid opno; /* 比较操作符 */
bool op_is_ne; /* 是否为<>操作符 */
Expr *expr; /* 比较表达式 */
Oid cmpfn; /* 比较函数 OID */
int op_strategy; /* 操作策略 */
} PartClauseInfo;
流程上,系统先对 SQL 进行解析生成 AST,再调用 gen_partprune_steps() 转换条件为剪枝步骤,最后通过 perform_pruning_base_step() 确定需扫描的分区并调整执行计划。


