PostgreSQL码农集散地

AI辅助 PolarDB内核学习 - 23 优化器plan模块概览

AI辅助 PolarDB内核学习 - 23 优化器plan模块概览

以下是针对PostgreSQL优化器plan模块的深度解析:

Image

文件功能详解:

  1. planner.c

  • 优化器总入口
  • 处理查询重写、路径生成、计划构建
  • 调用planmain.c启动主流程
  • planmain.c

    • 查询规划主控制器
    • 核心函数query_planner()
    • 协调基表处理、连接分解、路径生成
  • initsplan.c

    • 初始化基表RelOptInfo
    • 处理WHERE/JOIN-ON条件(distribute_qual_to_rels)
    • 构建约束条件链表
  • analyzejoins.c

    • 连接优化核心模块
    • 消除无效外连接(remove_useless_joins)
    • 处理连接顺序约束
  • createplan.c

    • 将最优路径转为执行计划
    • 生成SeqScan/IndexScan/NestLoop等节点
    • 处理投影、排序等操作
  • setrefs.c

    • 执行计划后期处理
    • 修复变量引用(set_plan_references)
    • 处理参数化路径
  • subselect.c

    • 子查询优化处理
    • 将ANY/EXISTS子查询转为半连接
    • 处理相关子查询去关联
  • planagg.c

    • 聚合优化专项处理
    • 将MIN/MAX转为LIMIT查询
    • 处理带有索引的极值查询

    数据流动示例:

    查询树 → planner.c → planmain.c → initsplan.c (构建基表信息)  
                          ↓  
                  analyzejoins.c (优化连接)  
                          ↓  
                  createplan.c (生成计划树)  
                          ↓  
                   setrefs.c (绑定变量)  
                          ↓  
                 最终执行计划输出  

    一、架构师视角 - 模块全景

    Image

    核心文件分工:

    • planner.c: 优化器入口,协调全流程
    • initsplan.c: 初始化基表信息,构建RestrictInfo
    • analyzejoins.c: 连接优化,消除无效连接
    • createplan.c: 将最优路径转换为可执行的Plan树
    • planagg.c: 聚合优化(如MIN/MAX转LIMIT查询)

    二、内核开发者视角 - 关键流程

    2.1 主流程(query_planner函数)

    Image


    2.2 条件推送(distribute_qual_to_rels)

    典型场景:WHERE条件处理

    // initsplan.c  
    distribute_qual_to_rels()  
    {  
    if (条件仅涉及单表)  
            加入baserestrictinfo  
    elseif(涉及多表)
            加入joininfo列表  
    if(mergejoinable)
            创建等价类  
    }  

    处理逻辑:

    1. 分离单表条件与连接条件
    2. 识别可下推条件
    3. 维护等价类信息

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

    3.1 统计信息利用

    统计类型
    影响决策
    示例
    pg_class.reltuples
    表基数估算
    SeqScan代价计算
    pg_stats.correlation
    索引有效性判断
    索引扫描选择
    pg_statistic.stakind1
    列数据分布
    条件选择性估算

    3.2 执行计划调优点

    EXPLAINSELECT * FROM orders WHERE customer_id=123AND total>100;  

    优化器可能行为:

    1. 优先使用customer_id索引
    2. 对total条件做索引条件过滤
    3. 根据数据相关性决定索引扫描或位图扫描

    四、重点模块解析

    4.1 连接消除(analyzejoins.c)

    处理逻辑:

    remove_useless_joins()  
    {  
        foreach 特殊连接信息  
    if (左连接可消除)  
                remove_rel_from_query()  
                重构连接树  
    }  

    示例场景:

    SELECT * FROM A LEFTJOIN B ON A.id=B.aid WHERE B.x ISNOTNULL;  
    -- 优化器会将LEFT JOIN转为INNER JOIN   PS: B.x 有not null约束才成立吧? 或者改成b.* is not null?  

    4.2 聚合优化(planagg.c)

    MIN/MAX优化流程:

    1. 识别MIN/MAX聚合
    2. 构建等价子查询
    -- 原查询  
    SELECTMIN(price) FROM products WHEREcategory=1;  

    -- 优化后等效  
    SELECT price FROM products   
    WHEREcategory=1AND price ISNOTNULL
    ORDERBY price LIMIT1;  

    关键判断条件:

    • 存在可用索引
    • 无影响结果的易失函数
    • 无GROUP BY等复杂结构

    4.3 计划生成(createplan.c)

    核心数据结构:

    typedefstructPlan {
        NodeTag     type;  
    double      startup_cost;  
    double      total_cost;  
        List       *targetlist;  
        List       *qual;  
    //...  
    } Plan;  

    生成过程:

    1. 选择最优路径(索引扫描/顺序扫描)
    2. 递归生成子节点计划
    3. 添加投影、排序等操作节点

    五、关键算法与数据结构

    5.1 动态规划搜索

    # make_one_rel 中的路径搜索  
    deffind_best_path():
    for level in1..n:  
    for each subset of size level:  
                generate_join_paths()  
                keep_cheapest_path()  

    5.2 代价模型

    代价计算公式:

    总代价 = 启动代价 + (行数 × 每行代价)  
    索引扫描代价 = random_page_cost × 选择率 + cpu_index_tuple_cost × 行数  

    5.3 等价类处理

    // 生成等价条件  
    generate_base_implied_equalities()  
    {  
        foreach 等价类  
    if (存在可合并条件)  
                生成隐含条件  
                加入约束列表  
    }  

    六、诊断与优化建议

    6.1 常见性能模式

    现象
    优化器应对策略
    DBA应对措施
    缺失统计信息
    默认选择顺序扫描
    ANALYZE更新统计
    高选择性条件
    优先使用索引
    创建合适索引
    多列相关条件
    使用复合索引
    调整索引列顺序
    外键连接
    识别外键约束优化连接顺序
    确保外键索引存在

    6.2 执行计划分析要点

    1. 确认基数估算准确性
    2. 检查索引选择合理性
    3. 识别不必要的排序操作
    4. 关注连接类型(Nested Loop/Hash Join/Merge Join)

    通过深入理解优化器代码结构,开发者能更好定位性能瓶颈,DBA可针对性优化SQL与数据库配置,架构师则能设计更合理的数据库架构。