这篇文章给大家分享的是有关Oracle如何解读执行计划的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
我先上一条语句,因为我觉得这条比较典型,所以我们就先用这条的执行计划来解读下执行计划。
然后是这条sql的我调整过的一个执行计划,我们本文主要是看执行计划的部分。
执行计划的开头
首先不好意思,我把sql_id,plan_hash_value忘截图了,基本上就是第一部分是执行计划的开头,有sql文本,sqlid,plan hash value,child number,这就不赘述了。
执行计划的正文
先说一下,这个正文的由来,为了能看到starts,a-rows,a-time,buffers,0men,1mem… 所以,使用了:
alter sesssion set statistics_level=all;
select * from dbms_xplan.display_cursor(null,null,'advanced allstat');
所以说我们这次主要说的是以dbms_xplan包来看的执行计划内容。
id,operation,name
我们先笼统的介绍一下这个3个的意思跟由来。
id:其实就是标识每一个步步骤的一个序号,就是跟个身份证似的,这个是从v$sql_plan的id列中得到的,并不代表他们执行的顺序,不过我稍后就会说阅读执行计划的顺序及其原理。
operation:是v$sql_plan中的operation列加上option列组合出来了,意思很浅显,说的就是这一步的操作。
name:就是v$sql_plan中的object_name,也很明白,就是这一步操作的对象名称。
现在我们来解释如果确定每一步的执行顺序。
口诀随便都能找到,就是看缩进,从左往右看,不对齐的,就是靠右的先执行;到上下平行的或者说对齐的(紧挨着的),那靠上的就比靠下的先执行,然后再继续右看,看到最右边的时候就完事儿。其实这个看不太明白我说的的,可以结合一下百度。
我直接说原理,当然也不是最底层实现的原理,那个现在还搞不来。
从左往右看,靠右的先执行是因为,一个缩进就代表了一对父子,儿子把自己的步骤得到的结果集整理好,上交给老子,就这么一回事儿。所以说0-8的执行顺序是8 7 6 5 4 3 2 1 0.(我这里又要道歉,我有点儿蠢了,没给大家选到那种有俩个
紧挨着的儿子的执行计划,所以这里先说原理,大家这块再结合下百度)
然后当你在找爹的路上往回走的时候,发现到了id=4这一步,往下看发现了个叔叔id=9,那这时候,4和9是并列的,也就是同辈儿的,那就先不着急再往3走,而是先去看看9有没有儿子,确实有一个,那按照靠右先执行,应该是10比9先执行。
但是问题是,4跟9谁先执行呢?因为并列就是上面的先执行,所以是4先执行。然后又有人问了,那9跟3又是怎么个顺序呀,9跟4是兄弟,4是3儿子,也就是说9也是3儿子,当然是儿子先做收集结果集喽。那现在得到的顺序就成了 8-7-6-5-4-10-9-3-2-1-0。
然而,当找到id=3得时候,又发现,这个爸爸也有个同辈儿的,id=11,但是这个11没正好就是个光棍儿,没儿子。那就排出3比11先执行。后面2 1 0都没兄弟了,所以最终的执行顺序是8-7-6-5-4-10-9-3-11-2-1-0。
这个说起来很枯燥,读起来简单其实,现在我就说下重点,这个到底是怎么个实现的。
其实这就是个二叉树,因为我第一次写博客,很多没考虑到,没有把v$sql_plan中的position跟parent_id,还有树的深度截图出来,很不好意思,所以口述下这俩个的图形表现。就是他们在上面这张执行计划图片表现:同一个缩进度的就是position值
一样的,只查一个缩进度的就是父子关系,也就是说id=3和id=11的parent_id都是2。
现在我们来画一下这个二叉树,二叉树就是只有俩个叉,所以你不可见到说谁不遵守计划生育偏要整3个孩子的。
看,每个块块里的数字就是id。画图可以让我们正着看执行计划,按id画出来图,然后再去理出执行计划的执行顺序。
首先,第一个孩子都是往左边画,第二个孩子才是挨着往右边画,找其中典型的部分来说明下,3下面的孩子是4,但是发现仅比3少一个缩进的,或者说与4同缩进的还有9。那先发现的3所以,3画左边,9画右边。而9下面的有10,所以10画9下面的左边。
那有人问了,照这么说,10跟5也是平行的,都比4少一个缩进,为啥不把10画4下面,因为紧挨着的是直系,往上找没有紧挨着的爸爸了才去找老王。所以就按照这个原则画,就画出了这个执行计划的本质。
然后,就是怎么遍历这棵树,后序遍历,别问为什么。后序遍历:先左后右再根。也就是说一直找到最左边的节点(找到第一个紧挨着的并行的亲兄弟的时候的最靠右的步骤,也就是这对亲兄弟靠上的哥哥,执行计划的入口),然后往右找(这就是找亲弟弟,这就是为啥靠上的先执行),
没有就往上找(找爹,缩进靠右的步骤先执行的原因)。
最后,我们来看上面这副图,是不是8-7-6-5-4-10-9-3-11-2-1-0?!你要真不知道后序遍历咋遍历,你百度百科,贼简单。
starts
这个就是真实的这一个步骤的总的执行次数,你看上面所有步骤都只执行了一次。
A-rows
这个就是这一个步骤真实返回的行数,actual rows。也就是说,最上面id=0那一步的a-rows就是这条查询最总返回来的行数。
A-time
这一步真实执行的时间。最上面id=0那一步的a-time就是这条查询的真实执行时间。
buffers
逻辑读,这里没显示出reads,但是还是说一下,reads就是物理读。
0mem,1mem
这是说的hash join,sort,group by使用的PGA的内存大小,具体我还没验证清楚,好像0mem是用的PGA内存,1mem是用的硬盘空间。
执行计划的Qurey Block Name
这里可能不太被大家注意,但是我觉得初学,这里还是应该看一看的。所谓查询块,说白了就是select的个数。这部分内容,- 前面的数字是步骤id。
我在这里用/*+ qb_name() */把查询块名固定了,就是方便自己阅读,其实要是不用这个,oracle会自己给查询块取名,而且也挺好懂的,都是以SEL$开头的,后面跟个数字。举个例子,大家看10 – 这一行,A@zong2,我们回执行计划找一找,id=10走了一个索引。
你可能上来就看执行计划的时候会说,诶我去,这个索引是谁的呀。这不qurey block这部分就告诉你了嘛!这查询块zong2上的a表的。这个zong2我要是取名儿,oracle会给它取名SEL$2,为啥?!因为简单的说,这是第二个select。
希望初学者看这里的时候自己对着一行一行的回原计划和原语句中找找。
对了,这里我们发现一个在语句中并没有的别名,from$_subquery$_007。我们回步骤4找一下,发现是VIEW,再看看它是qiurank1块儿的,那就很明白了,这个就是整个qiurank1这个子查询整体的一个view,说白了就是这么一整块,它并不是一个真实存在再oracle中的视图,
提醒大家一下,执行计划中别看见view就是视图。子查询不展开,也是被当view来对待的。
综上,我们是不是发现,你从这部分内容中,你可以发现具体操作的对象是谁的,是属于那个select的?!
好,但是这部分的用途到这里还不算完,我们接着往后看。
执行计划的OUTLINE部分
是不是看到这个/*+ */有点儿眼熟,没错,这就这条语句执行使用的hint,其实使用hint也就是来控制这部分。这部分其实是真的告诉你执行计划里面到底干了些什么,当然,有一些我还不太确定,比如merge(qba>qbb),no_access,这些目前就只能大致猜,原因很简单,
因为我也是个初学者。
那有人可能要问了,执行计划正文加上后面要介绍的谓词条件,不就已经告诉我们这条sql到底是怎么运作的吗?还看这个干嘛。关键就是,一般我们去关注执行计划,就是因为执行计划有问题了。所以,我们光知道执行计划是怎么个流程还不够,我们是不是还要知道怎么修改它,让
它按照我们理想的姿势走正确的道路?!对于老司机,人家一眼就知道该加啥hint,但是对于初学者,我们还是老老实实看看outline,看看到底该加什么hint,或者说该修改什么hint。
就这个图片我举个例子,看到倒数第三行,index_rs_asc那一行,这一行明确告诉你有一步是走了索引,这一步就是走了qiurank块里的x表的…索引,而且这个索引是RS_ASC:range scan升序的,说明走这个索引是正常从左往右走的而且还是个index range scan。
这一行下面,说了个啥?!是不是再说qiurank块里的x表索引扫描完了,然后用rowid回表是用batched这种多块扫描的方式回的表,自己可以去执行计划里面找找,就是id=8,7做的事情。还有往上看一行,leading那一行,这个hash join的顺序是不是跟执行计划里面体现的一毛一样,
那我们把这里后面的做一个顺序调整,然后加入到sql的hint当中,就像这条sql的hint所做的那样,是不是就能轻易的把hash join的顺序进行调整,你说你调整A和B不用看这个outline,那你调整子查询那一整块儿在hash join中的顺序,你准备咋调呀?!当然,肯定有别的办法,但是这样做是不是
就很方便,后面你调整好这块,然后在固定profile偷梁换柱的时候也方便呀。
所以我觉得这里该好好了解,我还没研究透,大家看到这篇文章的话,可以交流研讨共同进步。
执行计划的predicate information
这个正经没啥可说的,就很直白。前面的数字还是id,access代表用了索引,意思就是进入这个索引块跑了一圈儿。filter才是真正的过滤条件,也就是说这是回了表以后在表上的过滤条件。自己对这执行计划一步一步的看。
这里虽然很直白,但是很重要,如果你不看outline跟qurey block也就算了,这里你要是再不看,那读执行计划其实还挺费劲的,因为你要把所有涉及的表的列和索引的基本信息放在旁边,对着sql自己分析,就很累。而且有的时候,你觉得是这个索引走对了,比如说这条sql最后的条件,
你要是有一边日期笔误写粗了,比如多加了个空格,出来的执行计划还是一毛一样,但是predicate information就不一样了。所以说,如果能看到这部分,还是要好好看一下。
执行计划的column projection
这里我还是好好说一下,虽然内容不多,前面的数字还是id,keys就是说明这里有排序(这部分我后面学习明白了我再补充)。
大拿不一定看这里,但是对于初学者,看一看是有收获的。这部分就是每一个步骤操作的列以及该列的数据类型。道理很简单,但是我们能学到一些东西,我们能通过这部分和执行计划正文了解到,每一步到底是在操作哪些列,我们看到你真正的排序操作,是在8 7 6一系列过滤后才开始在5进行
分析函数的排序,在完成最后的形成一个整体view之前,8 7 6 5都是带着rowid在操作的。
反正看这里,就是能从另一个维度来看执行计划。还是希望初学者还是看一看,我觉得还是有收获的。
执行计划的note
这不多说了,直接上个我总结的的图,自己了解下吧。
感谢各位的阅读!关于“Oracle如何解读执行计划”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
原创文章,作者:kirin,如若转载,请注明出处:https://blog.ytso.com/205286.html