AI辅助 PolarDB内核学习 - 30 优化器(plan模块) 核心模块 subselect.c
AI辅助 PolarDB内核学习 - 30 优化器(plan模块) 核心模块 subselect.c
代码作用简述
subselect.c 是 PostgreSQL 查询优化器中的关键模块,负责处理子查询(SubLink)和公用表表达式(CTE)。它通过递归遍历计划树,将子查询转换为执行计划中的 SubPlan 或 InitPlan 节点,并优化 EXISTS 和 ANY 类型的子查询为连接操作。此外,该模块还支持 CTE 的内联化或初始化计划生成。
一、内核开发者视角 - 核心机制解析
1.1 子查询转换流程 (make_subplan)
关键逻辑:
子查询规划:通过 subquery_planner生成子查询的最佳路径。参数隔离:提取子查询所需的外部参数( plan_params),确保它们在父查询中正确传递。计划生成:调用 create_plan将路径转换为执行计划。SubPlan 或 InitPlan:根据子查询是否包含相关变量( parParam),决定生成SubPlan或InitPlan。
示例场景:
SELECT * FROMusersWHEREidIN (SELECT user_id FROM orders);
优化器行为:
检测到 IN子查询。规划子查询路径,提取外部参数。 生成 SubPlan或InitPlan,并替换原始子查询。
1.2 EXISTS 子查询优化 (convert_EXISTS_to_ANY)
关键点:
条件判断:若 EXISTS 子查询无目标列表且不引用外部变量,则可优化为 ANY 子查询。 示例:
SELECT * FROMusersWHEREEXISTS (SELECT1FROM orders WHERE orders.user_id = users.id);
优化器行为:
检测到 EXISTS 子查询。 简化子查询为目标列表为空的结构。 转换为等价的 ANY 子查询。
1.3 CTE 处理 (SS_process_ctes)
关键逻辑:
未引用 CTE:直接跳过处理。 内联化:若 CTE 不包含递归、副作用或外部引用,则将其内联为普通子查询。 InitPlan:否则,生成 InitPlan并添加到全局计划列表中。
示例:
WITH cte AS (SELECTid, nameFROMusers) SELECT * FROM cte;
优化器行为:
检测到 CTE 被引用。 若 CTE 可内联,则替换为普通子查询;否则生成 InitPlan。
二、架构师视角 - 模块设计与交互
2.1 模块分工
设计亮点:
统一接口:通过 make_subplan和SS_process_ctes统一处理子查询和 CTE。动态调整:支持多种特殊场景(如 EXISTS 转换、CTE 内联化),确保灵活性。 依赖管理:记录计划对用户定义函数、类型和表的依赖,用于缓存失效检测。
2.2 计划层次化
关键点:
上下文传递:通过 PlannerInfo和rtoffset参数,确保每一层计划的变量引用调整独立且正确。递归处理:从顶层计划开始,逐层调整子计划的变量引用。
三、用户视角 - 实际应用场景与收益
3.1 DBA 视角 - 执行计划优化
示例 1:ANY 子查询优化
EXPLAINSELECT * FROMusersWHEREidIN (SELECT user_id FROM orders);
优化器行为:
将 IN子查询转换为ANY子查询。使用哈希表加速匹配过程。
示例 2:CTE 内联化
WITH cte AS (SELECTid, nameFROMusers) SELECT * FROM cte;
优化器行为:
检测到 CTE 被引用。 若 CTE 可内联,则替换为普通子查询以减少开销。
3.2 应用开发者视角 - 性能调优技巧
常见问题:
WITH 或直接展开子查询 | ||
示例参数调整:
-- 控制子查询优化行为
SET enable_nestloop = on; -- 启用嵌套循环连接
SET from_collapse_limit = 8; -- 控制子查询展开深度
四、核心算法深度解析
4.1 EXISTS 子查询转换为连接 (convert_EXISTS_sublink_to_join)
关键逻辑:
条件判断:若 EXISTS 子查询不引用外部变量且无副作用,则可转换为 SEMI JOIN。 示例:
SELECT * FROMusersWHEREEXISTS (SELECT1FROM orders WHERE orders.user_id = users.id);
优化器行为:
检测到 EXISTS 子查询。 转换为 SEMI JOIN,提升性能。
4.2 CTE 内联化 (inline_cte)
关键点:
条件判断:若 CTE 不包含递归、副作用或外部引用,则可内联。 示例:
WITH cte AS (SELECTid, nameFROMusers) SELECT * FROM cte;
优化器行为:
检测到 CTE 被引用。 若 CTE 可内联,则替换为普通子查询。
4.3 参数管理 (SS_replace_correlation_vars)
关键逻辑:
变量替换:将相关变量( Var)替换为执行时参数(PARAM_EXEC)。示例:
SELECT * FROMusersWHEREEXISTS (SELECT1FROM orders WHERE orders.user_id = users.id);
优化器行为:
将 users.id替换为PARAM_EXEC。确保子查询在执行时正确引用父查询变量。
五、诊断与调优建议
5.1 执行计划分析
EXPLAIN (ANALYZE, VERBOSE) SELECT ...;
关注点:
是否存在冗余的 SubPlan或InitPlan节点。EXISTS 和 ANY 子查询是否被正确优化为连接。 内存使用是否符合预期。
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 初始化计划生成 (SS_make_initplan_from_plan)
关键点:
条件判断:若子查询无相关变量,则生成 InitPlan。示例:
SELECT * FROMusersWHEREEXISTS (SELECT1FROM orders WHERE orders.status = 'active');
优化器行为:
检测到子查询无相关变量。 生成 InitPlan,提前计算结果以减少重复执行。
七、总结
subselect.c 通过精细化的子查询优化和 CTE 处理,显著提升了复杂查询的执行效率。内核开发者需理解其递归调整机制,DBA 应结合统计信息与执行计划进行调优,而架构师则需关注模块间的协作以设计更灵活的查询引擎。