共计 4834 个字符,预计需要花费 13 分钟才能阅读完成。
这篇文章给大家分享的是有关 Oracle 如何解读执行计划的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。
我先上一条语句,因为我觉得这条比较典型,所以我们就先用这条的执行计划来解读下执行计划。
然后是这条 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 如何解读执行计划”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!