AI辅助 PolarDB内核学习 - 29 优化器(plan模块) 核心模块 setrefs.c
AI辅助 PolarDB内核学习 - 29 优化器(plan模块) 核心模块 setrefs.c
代码作用简述
setrefs.c 是 PostgreSQL 查询优化器中的关键模块,负责在生成执行计划的最后阶段调整变量引用(Var)、参数(Param)和表达式树,确保它们与最终的执行计划一致。它通过递归遍历计划树,修正目标列表(TargetList)和条件表达式(Quals),并处理子查询、外键、聚合函数等复杂场景。
一、内核开发者视角 - 核心机制解析
1.1 变量引用调整流程 (set_plan_references)
关键逻辑:
RTE 展平:将所有子查询的 RTE(Range Table Entry)合并到全局的 finalrtable中。变量偏移:根据 rtoffset调整 Var 的varno和varattno,确保其引用与展平后的 RTE 对应。子计划替换:对于无用的子查询扫描(如 SubqueryScan),直接替换为子计划以减少冗余。
示例场景:
SELECT * FROM (SELECTid, nameFROMusersWHERE age > 30) AS sub;
优化器行为:
将子查询 users的 RTE 合并到全局finalrtable。调整外部查询的目标列表,使其引用子查询的输出列。 如果子查询扫描无用,则直接替换为子计划。
1.2 连接节点变量调整 (set_join_references)
关键点:
变量分类:区分外侧(OUTER_VAR)和内侧(INNER_VAR)变量,确保连接语义正确。 条件匹配:通过 fix_join_expr匹配连接条件中的变量,并替换为目标列表中的列引用。
示例:
SELECT * FROM A JOIN B ON A.id = B.aid;
优化器行为:
检测到 A.id和B.aid分别属于外侧和内侧关系。将 A.id替换为 OUTER_VAR,B.aid替换为 INNER_VAR。
二、架构师视角 - 模块设计与交互
2.1 模块分工
设计亮点:
统一接口:通过 set_plan_refs递归处理所有计划节点类型(如SeqScan,IndexScan,Join等)。动态调整:支持多种特殊场景(如子查询扫描、外连接、窗口函数等),确保灵活性。 依赖管理:记录计划对用户定义函数、类型和表的依赖,用于缓存失效检测。
2.2 计划层次化
关键点:
上下文传递:通过 PlannerInfo和rtoffset参数,确保每一层计划的变量引用调整独立且正确。递归处理:从顶层计划开始,逐层调整子计划的变量引用。
三、用户视角 - 实际应用场景与收益
3.1 DBA 视角 - 执行计划优化
示例 1:子查询扫描优化
EXPLAINSELECT * FROM (SELECTid, nameFROMusersWHERE age > 30) AS sub;
优化器行为:
检测到子查询扫描无实际意义。 直接替换为子计划,减少不必要的 SubqueryScan节点。
示例 2:外连接变量调整
EXPLAINSELECT * FROM A LEFTJOIN B ON A.id = B.aid;
优化器行为:
检测到 LEFT JOIN条件中涉及外侧和内侧变量。将 A.id替换为 OUTER_VAR,B.aid替换为 INNER_VAR。
3.2 应用开发者视角 - 性能调优技巧
常见问题:
WITH 或直接展开子查询 | ||
示例参数调整:
-- 控制子查询优化行为
SET enable_nestloop = on; -- 启用嵌套循环连接
SET from_collapse_limit = 8; -- 控制子查询展开深度
四、核心算法深度解析
4.1 子查询扫描优化 (trivial_subqueryscan)
关键逻辑:
条件判断:若子查询扫描无条件过滤且目标列表完全映射子计划输出,则可删除该节点。 示例:
SELECT * FROM (SELECTid, nameFROMusers) AS sub;
优化器行为:
检测到子查询扫描无实际作用。 直接替换为子计划,避免额外开销。
4.2 窗口函数变量调整 (set_windowagg_runcondition_references)
关键点:
变量替换:将窗口函数(WindowFunc)替换为对应的目标列表列引用。 示例:
SELECTSUM(amount) OVER () AS total FROM transactions;
优化器行为:
将 SUM(amount)替换为目标列表中的列引用。减少窗口函数的重复计算。
4.3 聚合函数优化 (find_minmax_agg_replacement_param)
关键逻辑:
条件判断:若聚合函数为 MIN/MAX且满足特定条件(如单列聚合),则用参数替换以提升性能。示例:
SELECTMIN(id) FROMusers;
优化器行为:
检测到 MIN(id)可优化为参数。替换为参数引用,减少排序操作。
五、诊断与调优建议
5.1 执行计划分析
EXPLAIN (ANALYZE, VERBOSE) SELECT ...;
关注点:
是否存在冗余的 SubqueryScan或Append节点。窗口函数和聚合函数是否被正确优化。 内存使用是否符合预期。
5.2 参数调优
enable_nestloop | ||
enable_hashjoin | ||
work_mem |
示例:
-- 提升排序内存
SET work_mem = '8MB';
-- 强制禁用嵌套循环连接
SET enable_nestloop = off;
六、创新机制解读
6.1 动态依赖管理 (record_plan_function_dependency)
效果:
自动记录计划对用户定义函数的依赖,确保缓存计划在函数变更时失效。 示例:
CREATEFUNCTION my_func(x int) RETURNSintAS $$ ... $$ LANGUAGESQL;
SELECT my_func(id) FROMusers;
优化器行为:
检测到 my_func为用户定义函数。记录其依赖关系,确保函数变更时重新规划。
6.2 并行计划生成 (set_param_references)
关键点:
初始化参数管理:确保并行计划中的初始化参数(InitPlan)正确传递。 示例:
SELECT * FROMusersWHEREidIN (SELECT user_id FROM orders);
优化器行为:
检测到子查询 IN条件。生成初始化参数,并确保其在并行计划中正确传递。
七、总结
setrefs.c 通过精细化的变量引用调整和表达式优化,确保执行计划的正确性和高效性。开发者需理解其递归调整机制,DBA 应结合统计信息与执行计划进行调优,而架构师则需关注模块间的协作以设计更灵活的查询引擎。