openGauss内核分析:执行计划生成
摘要:SQL语句解析完成后被解析成Query结构,在进行优化时是以Query为单位进行的,Query的优化分为基于规则的逻辑优化(查询重写)和基于代价的物理优化(计划生成),主入口函数为subquery_planner。subquery_planner函数接收Query(查询树),返回一个Plan(计划树)。
本文分享自华为云社区《openGauss内核分析(六) 执行计划生成》,作者:Gauss松鼠会。
SQL语句解析完成后被解析成Query结构,在进行优化时是以Query为单位进行的,Query的优化分为基于规则的逻辑优化(查询重写)和基于代价的物理优化(计划生成),主入口函数为subquery_planner。subquery_planner函数接收Query(查询树),返回一个Plan(计划树)。
Plan* subquery_planner(PlannerGlobal* glob, Query* parse, PlannerInfo* parent_root, bool hasRecursion,
double tuple_fraction, PlannerInfo** subroot, int options, ItstDisKey* diskeys, List* subqueryRestrictInfo)
{
PlannerInfo* root = NULL;
Plan* plan = NULL; //返回结果
…
preprocess_const_params(root, (Node*)parse->jointree); // 常数替换等式
…
if (parse->hasSubLinks) {
pull_up_sublinks(root); //提升子链接
DEBUG_QRW("After sublink pullup");
}
/* Reduce orderby clause in subquery for join */
reduce_orderby(parse, false); //减少orderby
DEBUG_QRW("After order by reduce");
if (u_sess->attr.attr_sql.enable_constraint_optimization) {
removeNotNullTest(root); //删除NotNullTest
DEBUG_QRW("After soft constraint removal");
}
…
if ((LAZY_AGG & u_sess->attr.attr_sql.rewrite_rule) && permit_from_rewrite_hint(root, LAZY_AGG)) {
lazyagg_main(parse); // lazyagg重写
DEBUG_QRW("After lazyagg");
}
…
parse->jointree = (FromExpr*)pull_up_subqueries(root, (Node*)parse->jointree); //提升子查询
…
if (parse->setOperations) {
flatten_simple_union_all(root); //UNIONALL优化
DEBUG_QRW("After simple union all flatten");
}
…
expand_inherited_tables(root); //展开继承表
…
parse->targetList = (List*)preprocess_expression(root, (Node*)parse->targetList, EXPRKIND_TARGET); //预处理表达式
…
parse->havingQual = (Node *) newHaving; //处理HAVING子句
…
reduce_outer_joins(root); //外连接消除
…
reduce_inequality_fulljoins(root); //全连接重写
…
plan = grouping_planner(root, tuple_fraction); //主要的计划过程
…
return plan;
}
subquery_planner函数由函数standard_planner调用,standard_planner函数由exec_simple_query->pg_plan_queries->pg_plan_query->planner函数调用。standard_planner将Query(查询树)生成规划好的语句,可用于执行器实际执行。
PlannedStmt* standard_planner(Query* parse, int cursorOptions, ParamListInfo boundParams)
{
PlannedStmt* result = NULL; //返回结果
PlannerGlobal* glob = NULL;
double tuple_fraction;
PlannerInfo* root = NULL;
Plan* top_plan = NULL;
…
glob = makeNode(PlannerGlobal);
/* primary planning entry point (may recurse for subqueries) */
top_plan = subquery_planner(glob, parse, NULL, false, tuple_fraction, &root); //主规划过程入口
…
/* build the PlannedStmt result */
result = makeNode(PlannedStmt); //构造PlannedStmt
result->commandType = parse->commandType;
result->queryId = parse->queryId;
result->uniqueSQLId = parse->uniqueSQLId;
result->hasReturning = (parse->returningList != NIL);
result->hasModifyingCTE = parse->hasModifyingCTE;
result->canSetTag = parse->canSetTag;
result->transientPlan = glob->transientPlan;
result->dependsOnRole = glob->dependsOnRole;
result->planTree = top_plan; //执行计划
result->rtable = glob->finalrtable;
result->resultRelations = glob->resultRelations;
…
return result;
}
仍然以前文的join列子来说明
SELECT * FROM t1 inner JOIN t2 ON t1.c1 = t2.c1;复制
在planner函数打断点,用gdb查看standard_planner返回的PlannedStmt
(gdb) bt
#0 planner (parse=0x7fd93a410288, cursorOptions=0, boundParams=0x0) at planner.cpp:389
#1 0x0000000001936fbd in pg_plan_query (querytree=0x7fd93a410288, cursorOptions=0, boundParams=0x0, underExplain=false) at postgres.cpp:1197
#2 0x0000000001937381 in pg_plan_queries (querytrees=0x7fd939b81090, cursorOptions=0, boundParams=0x0) at postgres.cpp:1315
#3 0x000000000193a6b8 in exec_simple_query (query_string=0x7fd966ad2060 "SELECT * FROM t1 inner JOIN t2 ON t1.c1 = t2.c1;", messageType=QUERY_MESSAGE, msg=0x7fd931056210)
at postgres.cpp:2560
#4 0x0000000001947104 in PostgresMain (argc=1, argv=0x7fd93a2cf1c0, dbname=0x7fd93a2ce1f8 "postgres", username=0x7fd93a2ce1b0 "test") at postgres.cpp:8403
#5 0x0000000001890740 in BackendRun (port=0x7fd931056720) at postmaster.cpp:8053
#6 0x00000000018a00b1 in GaussDbThreadMain<(knl_thread_role)1> (arg=0x7fd97c55c5f0) at postmaster.cpp:12181
#7 0x000000000189c0de in InternalThreadFunc (args=0x7fd97c55c5f0) at postmaster.cpp:12755
#8 0x00000000024bf7d8 in ThreadStarterFunc (arg=0x7fd97c55c5e0) at gs_thread.cpp:382
#9 0x00007fd9a60cfdd5 in start_thread () from /lib64/libpthread.so.0
#10 0x00007fd9a5df8ead in clone () from /lib64/libc.so.6
(gdb) p *result
$14 = {type = T_PlannedStmt, commandType = CMD_SELECT, queryId = 0, hasReturning = false, hasModifyingCTE = false, canSetTag = true, transientPlan = false, dependsOnRole = false,
planTree = 0x7fd93a409d58, rtable = 0x7fd939b81660, …}
(gdb) p *result->planTree->lefttree
$46 = {type = T_SeqScan, plan_node_id = 2, parent_node_id = 1, exec_type = EXEC_ON_DATANODES, startup_cost = 0, total_cost = 1.03, plan_rows = 3, multiple = 1, plan_width = 8,…}
将Query规划后得到PlannedStmt


可以看到,Plannedstmt 与explain执行计划是一致的

openGauss内核分析:执行计划生成的更多相关文章
- spark sql 执行计划生成案例
前言 一个SQL从词法解析.语法解析.逻辑执行计划.物理执行计划最终转换为可以执行的RDD,中间经历了很多的步骤和流程.其中词法分析和语法分析均有ANTLR4完成,可以进一步学习ANTLR4的相关知识 ...
- sqlplus 分析执行计划
转载 http://xm-koma.iteye.com/blog/1048451 对于oracle9i,需要手工设置plustrace角色,步骤如下: 1.在SQL>connect sys/密码 ...
- 索引使用,分析初探。(explain分析执行计划,以及强制使用force index)
促使这次探索的初衷还是因为要对一个定时脚本性能进行优化. 脚本有两个指定状态分别是status, latest_process_status,和一个超期时间expire_time进行限制. 按照我以前 ...
- SQL Sever 2008性能分析之执行计划
一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试 2的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<Inside Microsoft SQL ...
- Oracle中获取执行计划的几种方法分析
以下是对Oracle中获取执行计划的几种方法进行了详细的分析介绍,需要的朋友可以参考下 1. 预估执行计划 - Explain PlanExplain plan以SQL语句作为输入,得到这条S ...
- SQL点滴27—性能分析之执行计划
原文:SQL点滴27-性能分析之执行计划 一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<I ...
- SQL性能分析之执行计划
一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<Inside Microsoft SQL S ...
- SQL Server 执行计划分析
当一个查询到达数据库引擎时,SQL Server执行两个主要的步骤来产生期望的查询结果: 第一步:查询编译,生成查询计划. 第二步:执行这个查询计划. 1. 用于演示分析执行计划的查询语句 /* 查询 ...
- 分析oracle的执行计划(explain plan)并对对sql进行优化实践
基于oracle的应用系统很多性能问题,是由应用系统sql性能低劣引起的,所以,sql的性能优化很重要,分析与优化sql的性能我们一般通过查看该sql的执行计划,本文就如何看懂执行计划,以及如何通过分 ...
- SqlServer 中如何查看某一个Sql语句是复用了执行计划,还是重新生成了执行计划
我们知道SqlServer的查询优化器会将所执行的Sql语句的执行计划作缓存,如果后续查询可以复用缓存中的执行计划,那么SqlServer就会为后续查询复用执行计划而不是重新生成一个新的执行计划,因为 ...
随机推荐
- LooklessControl
Lookless controls vs User Controls. Lookless controls usage patterns(LooklessControl与UserControl的比较. ...
- [Python急救站课程]获取星期字符串
如何获取星期字符串 weekstr = "星期一星期二星期三星期四星期五星期六星期日" weekid = eval(input("请输入星期数字(1~7):") ...
- Kubernetes 漫游:理解 ConfigMap
安装说明 通过 docker desktop 可以安装适用于单机和开发环境单机版的 K8S,如果 docker desktop 无法启动 Kubernates 通过以下方式解决: 一:添加国内镜像源 ...
- 【源码系列#02】Vue3响应式原理(Effect)
专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核推荐 欢迎各位ITer关注点赞收藏 Vue3中响应数据核心是 reactive , reactive 的实 ...
- Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo
前言 驱动写好后,用户层使用系统函数调用操作相关驱动从而实现与系统内核的关联,本篇主要就是理解清楚驱动如何让用户编程来实现与内核的交互. 杂项设备文件操作集 cd /usr/src/linux ...
- 【Android】学习day05|RadioButton
注意事项:当使用默认选中标签:check时,必须要给标签加id,否则失效. 这个没什么,挺简单的,就记录一下代码[监听事件] package com.example.app02; import and ...
- 从ClickHouse通往MySQL的几条道路
一.应用背景简介 ClickHouse 是 Yandex(俄罗斯最大的搜索引擎)开源的一个用于实时数据分析的基于列存储的数据库,其处理数据的速度比传统方法快 100-1000 倍.ClickHouse ...
- C++ Qt开发:Qt的安装与配置
Qt是一种C++编程框架,用于构建图形用户界面(GUI)应用程序和嵌入式系统.Qt由Qt公司(前身为Nokia)开发,提供了一套跨平台的工具和类库,使开发者能够轻松地创建高效.美观.可扩展的应用程序. ...
- [CF1854E] Game Bundles
题目描述 Rishi is developing games in the 2D metaverse and wants to offer game bundles to his customers. ...
- 牛客刷java记录第5天
第一题,下列代码运行结果是? class X { Y y = new Y(); public X() { System.out.print("X"); } } class Y { ...