PostgreSQL码农集散地

AI辅助 PolarDB内核学习 - 25 优化器(plan模块) 核心模块 planmain.c

AI辅助 PolarDB内核学习 - 25 优化器(plan模块) 核心模块 planmain.c

以下从多维度对PostgreSQL优化器核心模块planmain.c进行深度解析:

一、架构师视角 - 模块定位与流程

Image


二、内核开发者视角 - 核心代码解析

2.1 主流程(query_planner函数)

Image


2.2 关键数据结构

// 连接消除上下文(analyzejoins.c)  
typedefstruct {
    List       *joinlist;       // 原始连接列表  
    Relids      outer_rels;     // 外层关系集合  
bool        removed_any;    // 是否执行过删除  
} JoinRemovalContext;  

// 等价类信息(planmain.c)  
typedefstruct {
    List       *ec_members;     // 等价成员列表  
    Relids      ec_relids;      // 涉及的基表集合  
} EquivalenceClass;  

三、DBA视角 - 优化器行为特征

3.1 外连接优化示例

EXPLAINSELECT *  
FROM A LEFTJOIN B ON A.id=B.aid  
WHERE B.x ISNOTNULL;  

优化器行为:

  1. remove_useless_joins检测到WHERE条件使LEFT JOIN等价于INNER JOIN
  2. 重写连接类型为INNER JOIN   -- PS: 应该是 B.* is not null? 或 B.x 有not null约束?
  3. 优化后的执行计划减少不必要的NULL扩展

3.2 统计信息应用

统计指标
影响决策
示例
reltuples
连接顺序选择
小表作为驱动表
pg_stats.correlation
索引有效性判断
范围查询选择索引扫描
pg_class.relpages
并行度计算
parallel_workers设置

四、重点算法深度解析

4.1 连接树分解(deconstruct_jointree)

Image


4.2 等价类生成

voidgenerate_base_implied_equalities(PlannerInfo *root){  
    foreach(ec, root->eq_classes) {  
if (ec->ec_has_const) {  
// 生成形如A.x = 5的约束  
            distribute_restrictinfo_to_rels(...);  
        }  
if (ec->ec_broken)  
continue;  
// 生成跨表等价条件  
        generate_join_implied_equalities(...);  
    }  
}  

典型场景:

WHERE A.x = B.y AND B.y = 10  
→ 推导出A.x = 10  

五、并行查询处理

5.1 并行度决策逻辑

// planmain.c中并行安全检查  
final_rel->consider_parallel =  
    root->glob->parallelModeOK &&  
    is_parallel_safe(root, parse->jointree->quals);  

影响因素:

  1. 基表的parallel_safe标记
  2. 表达式中的并行不安全函数
  3. GUC参数force_parallel_mode

5.2 并行执行示例

CREATETABLE t (idint) WITH (parallel_workers = 4);  
EXPLAINSELECTCOUNT(*) FROM t;  
-- 可能生成并行计划:  
Finalize Aggregate  
-> Gather  
   -> Partial Aggregate  
      -> Parallel Seq Scan  

六、扩展机制

6.1 自定义优化规则

通过set_plan_references扩展点:

Plan *set_plan_references(...){  
if (plan->type == T_CustomScan) {  
// 处理自定义扫描类型  
        handle_custom_scan_refs(...);  
    }  
// 标准处理流程  
return plan;  
}  

6.2 连接消除扩展

// analyzejoins.c扩展示例  
staticbool
custom_join_is_removable(...)
{  
if (custom_check_condition(...))  
returntrue;  
returnfalse;  
}  

七、诊断与调优

7.1 执行计划分析要点

EXPLAIN (ANALYZE, BUFFERS)  
SELECT * FROM A JOIN B ON A.id=B.aid;  

关注点:

  1. 连接顺序是否最优
  2. 连接方法选择(Hash Join vs Merge Join)
  3. 内存使用是否合理

7.2 关键GUC参数

-- 优化器行为控制  
SET enable_nestloop = off;      -- 禁用嵌套循环  
SET join_collapse_limit = 8;    -- 控制连接重排序  
SET from_collapse_limit = 8;    -- 控制子查询展开  

八、代码演进建议

  1. 动态统计信息更新:
// 在build_base_rel_tlists中添加:  
if (need_stats_update(rel))  
    auto_analyze_rel(rel);  
  1. 机器学习代价预测:
# 在生成路径时调用预测模型  
cost = ml_predict_cost(path_features)  

通过深入理解planmain.c的实现,开发者可以更好地定制优化规则,DBA能精准识别执行计划瓶颈,架构师可设计更高效的查询模式。建议结合EXPLAIN输出与源码调试器(gdb)观察优化器决策过程。