AI辅助 PolarDB内核学习 - 23 优化器plan模块概览
AI辅助 PolarDB内核学习 - 23 优化器plan模块概览
以下是针对PostgreSQL优化器plan模块的深度解析:
文件功能详解:
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 (绑定变量)
↓
最终执行计划输出
一、架构师视角 - 模块全景
核心文件分工:
planner.c: 优化器入口,协调全流程initsplan.c: 初始化基表信息,构建RestrictInfoanalyzejoins.c: 连接优化,消除无效连接createplan.c: 将最优路径转换为可执行的Plan树planagg.c: 聚合优化(如MIN/MAX转LIMIT查询)
二、内核开发者视角 - 关键流程
2.1 主流程(query_planner函数)
2.2 条件推送(distribute_qual_to_rels)
典型场景:WHERE条件处理
// initsplan.c
distribute_qual_to_rels()
{
if (条件仅涉及单表)
加入baserestrictinfo
elseif(涉及多表)
加入joininfo列表
if(mergejoinable)
创建等价类
}
处理逻辑:
分离单表条件与连接条件 识别可下推条件 维护等价类信息
三、DBA视角 - 优化器行为特征
3.1 统计信息利用
3.2 执行计划调优点
EXPLAINSELECT * FROM orders WHERE customer_id=123AND total>100;
优化器可能行为:
优先使用customer_id索引 对total条件做索引条件过滤 根据数据相关性决定索引扫描或位图扫描
四、重点模块解析
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优化流程:
识别MIN/MAX聚合 构建等价子查询
-- 原查询
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;
生成过程:
选择最优路径(索引扫描/顺序扫描) 递归生成子节点计划 添加投影、排序等操作节点
五、关键算法与数据结构
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 常见性能模式
6.2 执行计划分析要点
确认基数估算准确性 检查索引选择合理性 识别不必要的排序操作 关注连接类型(Nested Loop/Hash Join/Merge Join)
通过深入理解优化器代码结构,开发者能更好定位性能瓶颈,DBA可针对性优化SQL与数据库配置,架构师则能设计更合理的数据库架构。