AI辅助 PolarDB内核学习 - 27 优化器(plan模块) 核心模块 analyzejoins.c
AI辅助 PolarDB内核学习 - 27 优化器(plan模块) 核心模块 analyzejoins.c
代码作用简述
analyzejoins.c 是 PostgreSQL 查询优化器的核心模块之一,专注于连接树的简化和优化。它通过分析连接条件、基表属性以及外键约束等信息,实现以下功能:
消除无用连接:识别并移除那些不会影响查询结果的连接(如LEFT JOIN中可省略的右表)。 简化半连接:将满足唯一性条件的半连接(SEMI JOIN)转化为普通INNER JOIN。 保证语义正确性:在优化过程中确保连接语义不受破坏,同时提升执行计划效率。
一、内核开发者视角 - 核心机制解析
1.1 消除无用连接 (remove_useless_joins)
关键逻辑:
判断连接是否可移除:通过 join_is_removable()检查连接条件是否唯一化右表,且右表未被上层引用。移除无用关系:调用 remove_rel_from_query()删除右表,并更新所有相关数据结构(如attr_needed和SpecialJoinInfo)。
示例场景:
SELECT *
FROM A LEFTJOIN B ON A.id = B.aid
WHERE B.* ISNOTNULL;
优化器行为:
检测到 B.* IS NOT NULL条件使LEFT JOIN等价于INNER JOIN。将 LEFT JOIN转换为INNER JOIN,减少不必要的 NULL 行生成。
1.2 简化半连接 (reduce_unique_semijoins)
关键逻辑:
检测半连接唯一性:通过 innerrel_is_unique_for()检查右表是否对左表唯一。转换连接类型:若右表唯一,则将 SEMI JOIN 转换为 INNER JOIN。
示例场景:
SELECT *
FROM A
WHEREEXISTS (SELECT1FROM B WHERE A.id = B.aid);
优化器行为:
检测到 A.id = B.aid且B表具有唯一索引。将 SEMI JOIN 转换为 INNER JOIN,减少不必要的重复行检查。
二、架构师视角 - 模块设计与交互
设计亮点:
动态条件延迟:通过 check_outerjoin_delay确保条件在正确层级应用,避免破坏外连接语义。唯一性证明:利用索引和等价类信息( relation_has_unique_index_for),证明右表唯一性。多级缓存:通过 unique_for_rels和non_unique_for_rels缓存唯一性验证结果,减少重复计算。
数据流:
输入: PlannerInfo和连接树(joinlist)。输出:优化后的连接树。 中间状态:更新 RelOptInfo和SpecialJoinInfo的相关字段。
三、用户视角 - 实际应用场景与收益
3.1 DBA 视角 - 执行计划优化
示例 1:消除无用连接
EXPLAINSELECT *
FROM orders o
LEFTJOIN customers c ON o.cid = c.id
WHERE c.id ISNOTNULL;
优化前:
左连接生成 NULL 行。 过滤条件 c.id IS NOT NULL导致部分 NULL 行被丢弃。
优化后:
左连接转为内连接,减少不必要的 NULL 行生成。
示例 2:简化半连接
EXPLAINSELECT *
FROM A
WHEREEXISTS (SELECT1FROM B WHERE A.id = B.aid);
优化前:
半连接可能导致重复行检查。
优化后:
若 B.aid具有唯一索引,则直接使用 INNER JOIN,提升性能。
3.2 应用开发者视角 - 性能调优技巧
常见问题:
示例参数调整:
-- 控制连接重排序
SET join_collapse_limit = 8;
-- 强制禁用遗传算法优化
SET geqo = off;
四、核心算法深度解析
4.1 唯一性证明 (is_innerrel_unique_for)
关键点:
条件过滤:仅考虑 mergejoinable的等值条件(如=)。左右表匹配:确保条件形式为 outer op inner或inner op outer。唯一性验证:通过 rel_is_distinct_for()检查右表是否唯一。
示例:
SELECT *
FROM A
JOIN B ON A.id = B.aid;
假设 B(aid) 上存在唯一索引,则优化器可以安全地认为 B 表唯一。
4.2 外键约束匹配 (match_foreign_keys_to_quals)
关键点:
外键发现:通过 root->fkey_list获取外键信息。条件匹配:将外键约束与查询条件关联,提升多表连接的选择性估算精度。
示例:
SELECT *
FROM A
JOIN B ON A.fk = B.pk;
优化器利用外键约束 A(fk) REFERENCES B(pk),精确估算连接基数。
五、诊断与调优建议
5.1 执行计划分析
EXPLAIN (ANALYZE, VERBOSE) SELECT ...;
关注点:
是否存在多余的 NULL 行生成。 半连接是否被正确转换为 INNER JOIN。 条件是否下推至正确层级。
5.2 参数调优
join_collapse_limit | ||
from_collapse_limit | ||
geqo_threshold |
示例:
-- 提升连接重排序深度
SET join_collapse_limit = 16;
-- 禁用遗传算法优化
SET geqo = off;
六、创新机制解读
6.1 动态条件延迟 (check_outerjoin_delay)
效果:
防止条件被错误地下推至外连接可空侧,导致语义错误。 示例: LEFT JOIN中的条件必须在连接后应用。
6.2 LATERAL 引用追踪 (find_lateral_references)
效果:
确保 LATERAL 子查询中的外部变量正确传递。 示例:
SELECT *
FROM A, LATERAL (SELECT * FROM B WHERE B.x = A.y) AS sub;
优化器会将 A.y 标记为需要提前投影。
七、总结
analyzejoins.c 通过精细化的条件分析和语义验证,实现了连接树的高效优化。开发者需理解条件延迟与唯一性证明机制,DBA 应结合统计信息与执行计划进行调优,而架构师则需关注模块间的协作以设计更高效的查询引擎。