函数间关系的主要逻辑如下:
planner(){――第(1)层
standard_planner(){――第(2)层
subquery_planner(){――第(3)层
pull_up_sublinks(); //上拉子链接
pull_up_subqueries(); //上拉子查询
preprocess_expr
reduce_outer_joins(); //消除外连接
//以上部分的代码,主要完成逻辑优化的工作
grouping_planner(){――第(4)层 //生成查询计划
query_planner(){――第(5)层
make_on
set_base_rel_pathlist(): //找出所有访问基表的方法,包括顺序扫描和索引扫描
make_rel_from_joinlist (){――第(7)层
//决定使用什么查询优化的算法(遗传算法还是动态规划还是用户自定义的算法)生成一棵查询树的所有访问路径,得到最优的路径
//如果定义的遗传算法的连接顺序选择,那么采用它;否则,如下
standard_join_search (){――第(8)层 //实现动态规划算法,为一个查询找出所有可能的连接路径
join_search_on
make_join_rel () {――第(10)层 //查找或创建一个RelOptInfo节点,用于表示2个关系的连接结果,把2个关系的连接路径加入这个RelOptInfo节点
build_join_rel() {――第(11)层
Find or build the join RelOptInfo
Consider paths using each rel as both outer and inner //尝试把每个关系作为外关系、作为内关系(一个关系作为一次连接的内关系或外关系对效率有影响)
}――第(11)层结束,build_join_rel()
}――第(10)层结束,make_join_rel ()
make_rels_by_clause_joins(); //会调用make_join_rel ()
make_rels_by_clauseless_joins();//会调用make_join_rel ()
}――第(9)层结束,join_search_on
set_cheapest(rel); //被循环调用,得到每一个关系上的花费最低的路径
}――第(8)层结束,standard_join_search ()
}――第(7)层结束,make_rel_from_joinlist ()
}――第(6)层结束,make_on
}――第(5)层结束,query_planner() //返回值是void,但参数中有2个参数(cheapest_path, sorted_path)返回和最优的查询计划路径
optimize_minmax_aggregates(); //对min、max聚集计划优化
create_plan(); //工具求解得到的最优路径,创建(初步的)查询计划
//添加相应的计划节点(包括agg、group、distinct、sort、limit),生成完整的查询计划
}――第(4)层结束,grouping_planner()
}――第(3)层结束,subquery_planner()
set_plan_references(); //一些清理辅助工作
}――第(2)层结束,standard_planner()
}――第(1)层结束,planner()
以上是查询优化的整体流程,在进入第6层之前是的逻辑查询计划生成,如子查询的消除等逻辑优化策略都是在这个阶段之前完成的。
第6层之后是物理查询计划生成,利用动态规划(dynamic programming)的算法,实现多个关系的访问方式确定(顺序访问、索引访问、TID访问)、连接方式确定(嵌套循环连接算法、归并连接算法、hash连接算法)、连接顺序选取(左深树、右深树、紧密树)。
对于连接顺序,PG解释如下(可留心红色字体标明的决定连接顺序的方法的特点):
We consider left-handed plans (the outer rel of an upper join is a joinrel,
but the inner is always a single list item); right-handed plans (outer rel
is always a single item); and bushy plans (both inner and outer can be
joins themselves). For example, when building {1 2 3 4} we consider
joining {1 2 3} to {4} (left-handed), {4} to {1 2 3} (right-handed), and
{1 2} to {3 4} (bushy), among other choices.
生成join关系的同时,把可用的生成路径都添加到每个关系(包括基表和连接生成的表)的pathlist中,每个path除了表示连接的方法,还包含各方面(启动代价,总的执行代价,含某种顺序的代价,unique的代价等)代价估计的结果。当所有的有用的路径被添加之后,通过代价的比较,选出代价最小的最佳路径作为上层选取的贪婪的依据,每层都如此处理,直至顶层时,最佳查询路径被选取出来,这是一个唯一的结果表,至此,基于代价的查询优化完成。