PostgreSQL查询优化器之grouping_planner
grouping_planner主要做了3个工作:
- 对集合进行处理
- 对非SPJ函数进行优化
- 对SQL查询语句进行物理优化
grouping_planner实现代码如下:
static void
grouping_planner(PlannerInfo *root, bool inheritance_update,
double tuple_fraction)
{
/* 如果存在limit,offset,元组片段因子要改小 */
if (parse->limitCount || parse->limitOffset)
{
tuple_fraction = preprocess_limit(root, tuple_fraction,
&offset_est, &count_est);
}
/* Make tuple_fraction accessible to lower-level routines */
root->tuple_fraction = tuple_fraction;
//判断是否存在集合操作,如何存在,则处理集合运算。
if (parse->setOperations)
{
//会把集合语句按照集合操作符(差,并,交)分割SQL语句,
//然后调用为每一个独立的部分调用subquery_planner,
//所以Postgresql几乎不支持集合优化
//current_rel = plan_set_operations(root);
//顺便求出路径排序
root->sort_pathkeys = make_pathkeys_for_sortclauses(root,
parse->sortClause,
tlist);
}
else//非集合操作
{
/* ORDER BY和GROUP BY同时存在,先GROUP BY,在ORDER BY */
if (parse->groupingSets)
{
groupclause = preprocess_groupclause(root,
linitial(current_sets));
}
/* 对目标列进行处理*/
tlist = preprocess_targetlist(root, tlist);
//提前执行带有max/min的聚合函数子句
if (parse->hasAggs)
preprocess_minmax_aggregates(root, tlist);
}
/*最优路径*/
current_rel = query_planner(root, tlist,
standard_qp_callback, &qp_extra);
//为max/min生成执行计划
if (parse->hasAggs)
preprocess_minmax_aggregates(root, tlist);
}
}
query_planner生成最优查询路径
产生两个最优查询路径,主要是cheatest_path(未排序)和sorted_path(排序)
RelOptInfo *
query_planner(PlannerInfo *root, List *tlist,
query_pathkeys_callback qp_callback, void *qp_extra)
{
/*
* If the query has an empty join tree, then it's something easy like
* "SELECT 2+2;" or "INSERT ... VALUES()". Fall through quickly.
*/
if (parse->jointree->fromlist == NIL)
{
/*
* We still are required to call qp_callback, in case it's something
* like "SELECT 2+2 ORDER BY 1".标准化其他排序键,例如ORDER BY,GROUP BY
*/
root->canon_pathkeys = NIL;
(*qp_callback) (root, qp_extra);
return final_rel;
}
//初始化ROOT成员
/*找出所有基本表,放入simple_rte_array */
setup_simple_rel_arrays(root);
/*找出所有基本表,放入生成基本关系*/
add_base_rels_to_query(root, (Node *) parse->jointree);
//分解where和join中的约束条件,构建连接树
joinlist = deconstruct_jointree(root);
/*检查外连接子句,把外连接的约束条件分发到对应关系上
* ,看源码好像没有推到join关系上,而是推到join关系的子关系上
*/
reconsider_outer_join_clauses(root);
/*处理隐含约束条件*/
generate_base_implied_equalities(root);
/*去除无用连接*/
joinlist = remove_useless_joins(root, joinlist);
/*完成多表链接,采用动态规划和遗传算法 */
final_rel = make_one_rel(root, joinlist);
return final_rel;
}
deconstruct_jointree构造连接树函数
deconstruct_jointree用于分解树上的连接结构,分解方式为:把where和join中每个子句加入一个list中,然后把约束条件分配到每个关系上。一是把限制条件分配到基本关系上,二是把连接条件分配到连接关系上。这些本质上是逻辑优化阶段的“谓词下推操作”。但是由于此时还没有构造join关系,所以不能推到join关系上
static List *
deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
Relids *qualscope, Relids *inner_join_rels,
List **postponed_qual_list)
{
if (IsA(jtnode, RangeTblRef))
{
//构造只有一个节点的关系
joinlist = list_make1(jtnode);
}
else if (IsA(jtnode, FromExpr))
{
//递归构造每一个From子句,然后把结果下推
/*
* Now process the top-level quals.
*/
foreach(l, (List *) f->quals)
{
//还构建了RestrictInfo
distribute_qual_to_rels(root, qual,
false, below_outer_join, JOIN_INNER,
*qualscope, NULL, NULL, NULL,
postponed_qual_list);
}
}
else if (IsA(jtnode, JoinExpr))
{
//递归构造join两边
switch (j->jointype)
{
case JOIN_INNER:
case JOIN_ANTI:
case JOIN_FULL:
default:
}
/*处理join下推*/
foreach(l, my_quals)
{
Node *qual = (Node *) lfirst(l);
distribute_qual_to_rels(root, qual,
false, below_outer_join, j->jointype,
*qualscope,
ojscope, nonnullable_rels, NULL,
postponed_qual_list);
}
}
return joinlist;
}
reconsider_outer_join_clauses
分发外连接子句的约束条件
generate_base_implied_equalites
找出隐含条件,进一步谓词下推
make_one_rel 构造多表连接路径并选择最优路径的函数
RelOptInfo *
make_one_rel(PlannerInfo *root, List *joinlist)
{
/* Mark base rels as to whether we care about fast-start plans */
set_base_rel_consider_startup(root);
//为每个基本关系估计大小
set_base_rel_sizes(root);
//为每个基本关系生成RelOptInfo结构,并且生成访问路径放在path,这是单表/子查询的最佳扫描方式.
set_base_rel_pathlists(root);
/*返回一个最终的连接所有表的RelOptInfo */
rel = make_rel_from_joinlist(root, joinlist);
/*
* The result should join all and only the query's base rels.
*/
Assert(bms_equal(rel->relids, root->all_baserels));
return rel;
}
make_rel_from_joinlist
joinlist是从where和join on子句找出能做连接操作的对象
static RelOptInfo *
make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
{
/*
* Construct a list of rels corresponding to the child joinlist nodes.
* This may contain both base rels and rels constructed according to
* sub-joinlists.
*/
initial_rels = NIL;
foreach(jl, joinlist)
{
if (IsA(jlnode, RangeTblRef))//范围表直接找出要连接的关系
{
int varno = ((RangeTblRef *) jlnode)->rtindex;
thisrel = find_base_rel(root, varno);
}
else if (IsA(jlnode, List))//遍历子查询
{
/* Recurse to handle subproblem */
thisrel = make_rel_from_joinlist(root, (List *) jlnode);
}
initial_rels = lappend(initial_rels, thisrel);
}
if (levels_needed == 1)
{
}
else
{
root->initial_rels = initial_rels;
if (join_search_hook)
return (*join_search_hook) (root, levels_needed, initial_rels);//用户自定义
else if (enable_geqo && levels_needed >= geqo_threshold)
return geqo(root, levels_needed, initial_rels);//遗传算法
else
return standard_join_search(root, levels_needed, initial_rels);//动态规划
}
}
动态规划算法
例如:有一条SQL语句
SELECT * FROM A,B,C,D where A.a=B.a and ...
每层的关系如下:
- 第四层:ABCD
- 第三层:ABC,ACD,BCD
- 第二层:AB,AC,AD,BC,BD...
- 第一层:A,B,C,D
RelOptInfo *
standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
{
int lev;
RelOptInfo *rel;
/* root->join_rel_level[j]存放的是第j层的连接路径,
* 如果有n个关系,最大链接层数就是n。
*/
root->join_rel_level = (List **) palloc0((levels_needed + 1) * sizeof(List *));
root->join_rel_level[1] = initial_rels;//初始层数
for (lev = 2; lev <= levels_needed; lev++)
{
ListCell *lc;
/*使用动态规划求第lev层的所有关系,采用左深树和紧密熟的方式。N=N-1 +1;N=N-k + k */
join_search_one_level(root, lev);
/*
* Run generate_gather_paths() for each just-processed joinrel. We
* could not do this earlier because both regular and partial paths
* can get added to a particular joinrel at multiple times within
* join_search_one_level. After that, we're done creating paths for
* the joinrel, so run set_cheapest().
*/
foreach(lc, root->join_rel_level[lev])
{
rel = (RelOptInfo *) lfirst(lc);
//为lev层每个关系求最优路径
set_cheapest(rel);
}
}
rel = (RelOptInfo *) linitial(root->join_rel_level[levels_needed]);
root->join_rel_level = NULL;
return rel;
}
PostgreSQL查询优化器之grouping_planner的更多相关文章
- PostgreSQL查询优化简介
简介 PostgreSQL查询优化器执行过程 语法分析:生成查询树 语义检查:对SQL表达的语义进行检查 查询优化 视图重写 逻辑优化:子查询优化,条件化简,等价谓词重写,连接消除,得到逻辑计划 物理 ...
- PostgreSQL查询优化逻辑优化之其他
上一节我们介绍了PostgreSQL的子查询优化,子查询优化把一部分可以优化的子查询上拉到主查询成为join. preprocess_expression 将表达式(目标列,where,join,ha ...
- PostgreSQL查询优化之子查询优化
子查询优化 上拉子连接 上拉子连接主要是把ANY和EXIST子句转换为半连接 void pull_up_sublinks(PlannerInfo *root) { Node *jtnode; //子连 ...
- 关系型数据库工作原理-查询优化器之数据访问方式(翻译自Coding-Geek文章)
本文翻译自Coding-Geek文章:< How does a relational database work>.原文链接:http://coding-geek.com/how-data ...
- 关系型数据库工作原理-查询优化器之索引(翻译自Coding-Geek文章)
本文翻译自Coding-Geek文章:< How does a relational database work>.原文链接:http://coding-geek.com/how-data ...
- Mysql查询优化器之关于子查询的优化
下面这些sql都含有子查询: mysql> select * from t1 where a in (select a from t2); mysql> select * from (se ...
- Mysql查询优化器之关于JOIN的优化
连接查询应该是比较常用的查询方式,连接查询大致分为:内连接.外连接(左连接和右连接).自然连接 下图展示了 LEFT JOIN.RIGHT JOIN.INNER JOIN.OUTER JOIN 相关的 ...
- Mysql查询优化器之基本优化
对于一个SQL语句,查询优化器先看是不是能转换成JOIN,再将JOIN进行优化 优化分为: 1. 条件优化 2.计算全表扫描成本 3. 找出所有能用到的索引 4. 针对每个索引计算不同的访问方式的成本 ...
- 数据库查询优化器的艺术:原理解析与SQL性能优化
数据库查询优化器的艺术 作者:李海翔 Oracle公司MySQL全球开发团队.资深专家 简单的浏览了一遍,由于以前没有接触过SQL优化这些知识,读起来还是非常吃力的,不过收获还是很大的. 作者通过对M ...
随机推荐
- [UWP]本地化入门
1. 前言 上一篇文章介绍了各种WPF本地化的入门知识,这篇文章介绍UWP本地化的入门知识. 2. 使用resw资源文件实现本地化 在以前的XAML平台,resx资源文件是一种很方便的本地化方案,但在 ...
- 自学Zabbix3.9.1-模板Templates-创建
自学Zabbix3.9.1-模板Templates-创建 1. 模板介绍 模板是一组可以方便地应用于多个主机的实体. 实体可能是: 项目 触发器 图 应用程序 屏幕(因为Zabbix 2.0) 低层次 ...
- 工作随笔——Golang interface 转换成其他类型
新的公司,新的氛围.一年了,打算写点什么.so,那就写google的golang语言吧. 最最最基础的语法结构见go语言菜鸟教程 接下来写点菜鸟教程没有的. go语言的设计者认为:go语言必须让程序员 ...
- iOS SVN出现的问题,在mac使用Cornerstone中无法提交提交失败处理。。。
问题一: Description : An error occurred while contacting the repository. Suggestion : The server may be ...
- cell上添加倒计时,以及时差问题的解决
原址 http://www.cnblogs.com/zhangmaliang/p/5102518.html 最近项目中用到了tableView的多个cell倒计时系统问题,本觉得很简单的一个事,一做发 ...
- DeepLearning.ai学习笔记汇总
第一章 神经网络与深度学习(Neural Network & Deeplearning) DeepLearning.ai学习笔记(一)神经网络和深度学习--Week3浅层神经网络 DeepLe ...
- Git 企业开发者教程
为什么要写这样一个面向企业开发者的Git教程?这个问题也困扰我自己很久.其实我使用git的时间也不短了,但是就和正在阅读本文的每一位一样,常用的基本就是那么几个(git clone, git pu ...
- Zabbix自动发现java进程
一:简介 使用Python psutil模块,查找java模块,并获取启动命令,结合zabbix监控自动监控.点击下载 二:操作 发现脚本 #!/usr/bin/env python # coding ...
- SQL Server 2016 行级别权限控制
背景 假如我们有关键数据存储在一个表里面,比如人员表中包含员工.部门和薪水信息.只允许用户访问各自部门的信息,但是不能访问其他部门.一般我们都是在程序端实现这个功能,而在sqlserver2016以后 ...
- Mysql中字符集总结
有时候,在Mysql数据库中会经常遇到乱码的问题,现在普遍的做法就是全部强行把编码格式都设置成utf8模式,就可以解决这个问题,以前是知其然,不知其所以然,今天我就稍微研究了下Mysql的字符集. 就 ...