分析执行计划是解决ORACLE慢SQL的重要手段。获取到ORACLE的执行计划后,经验丰富的DBA根据连接方式、访问路径等可能一眼就能看出性能问题在哪里;但新手DBA可能不清楚执行计划该从哪里看,也不清楚执行计划的顺序是什么。所以这篇文章想尽可能的说清楚怎么看ORACLE执行计划顺序。
Table of Contents
1.执行计划样例
这里有一个执行计划样例,下文所有的讲解都是基于如下执行计划进行的。可以先看一下,先尝试自己进行排序。首先说明的是Id列数字并不是执行计划的顺序号。

2.基础知识了解
2.1.父子兄弟节点
Oracle的执行计划是树形结构,执行顺序类似于二叉树。理解执行计划中的父节点、子节点、兄节点、弟节点是至关重要的。
2.1.1.父子节点

可以根据执行计划缩进来判断父子节点。如上图所示,可以看到一条红色箭头,下面用Id号来代替Operation步骤。
1 比 0 缩进一格,且 0 在上方;则 1 是 0 的子节点,0 是 1 的父节点。
2 比 1 缩进一格,且 1 在上方;则 2 是 1 的子节点,1 是 2 的父节点。
以此类推…
10 比 9 缩进一格,且 9 在上方;则 10 是 9 的子节点,9 是 10 的父节点。
10 比 11 缩进一格,且 11 在下方,10 在上方,则 10 与 11 无父子关系。
也可以说 10 与 11 无任何关系,唯一的关系就是在同一个树形结构上。
2.1.2.兄弟节点

同样,可以根据执行计划的缩进来判断兄弟节点。如上图所示,可以看到4条红色箭头,下面用Id号来代替Operation步骤。
9 与 11 缩进相同,9在上,11在下;则9是兄节点,11是弟节点。
8 与 12 缩进相同,8在上,12在下;则8是兄节点,12是弟节点。
7 与 13 缩进相同,7在上,13在下;则7是兄节点,13是弟节点。
6 与 15 缩进相同,6在上,15在下;则6是兄节点,15是弟节点。
新手可能会有疑问:为什么上文中的 14 与 8、12缩进相同,但却不是兄弟节点呢?
这是因为13在14上方,14比13缩进一格,所以14是13的子节点,与8、12没有兄弟关系。
2.2.深度
这里的深度就是上文提到的缩进,代表着执行计划某一个步骤的深度。
例如上文描述的 Id 为5的步骤,相比与 Id 为0的步骤有5个缩进,其深度就是5;这也代表着此步骤有5层父步骤(Id为4、3、2、1、0)。
可以通过下面的SQL查看执行计划步骤的深度值。其中id是执行计划中的Id号,parent_id是其父节点的Id号,depth就是深度值。
select
sp.id
sp.parent_id,
sp.depth,
sp.operation|| '' || sp.options as operation,
sp.object_name
from v$sql_plan sp
where sp.sql_id = 'xxxx'
2.3.position
position代表拥有相同父节点的兄弟节点执行顺序。
在树形结构中,树中每个级别最左的叶节点最先执行。结合起来理解是在树形结构中左为兄、右为弟,在执行计划列表中上为兄、下为弟。
可以用下面的SQL查询执行计划的position。
select
sp.id
sp.parent_id,
sp.depth,
sp.postion,
sp.operation|| '' || sp.options as operation,
sp.object_name
from v$sql_plan sp
where sp.sql_id = 'xxxx'
SQL输出结果如下图。可以发现Id为9和Id为11的步骤拥有相同的父节点8,且深度相同;其中Id为9的position值是1,Id为11的position值是2,这表示Id为9的步骤先于Id为11的步骤执行。

这体现在树形结构中,Id为9的步骤是左节点,Id为11的步骤是右节点;体现在执行计划列表中则是Id为9的步骤在上,Id为11的步骤在下(这一点大家在前文中的执行计划列表截图中可以看得出)。
3.树形结构转换
前文讲了很多次树形结构,但新手可能对此并不了解,脑海中无法形成概念。我自己早些时候也是查了很多资料去理解数据结构中的树,所以在这里和大家分享将执行计划转换成树形结构去加深理解。
执行计划转换成树形结构过程拆解。
(1)拆解步骤1
前文已知执行计划每个步骤的深度,其中最深的是Id=10的步骤(这也是执行计划的入口)。
前文已知 10 的父节点是9,9的弟节点是11,左为兄、右为弟。
前文已知9与11的父节点是8。
所以,此时树形图如下:

(2)拆解步骤2
8与12是兄弟节点,8为兄、12为弟,左为兄、右为弟。
8与12的父节点是7。
所以,此时树形图如下:

(3)拆解步骤3
7与13是兄弟节点,7为兄、13为弟,左为兄、右为弟。
7与13的父节点是6。
所以,此时树形图如下所示:

(4)拆解步骤4
6与15是兄弟节点,6为兄、15为弟,左为兄、右为弟。
6与15的父节点是5。
13是14的父节点。
所以,此时树形图如下所示:

(5)拆解步骤5
Id为0、1、2、3、4依次为后一位的父节点,0是根节点。
所以最终的树形结构如下所示:

(6)树形结构总结
到第(5)步,这个执行计划的完整树形结构已经呈现出来了。这里再总结下根据树形结构如何查看执行顺序,也就是数据结构中的树形顺序。
① 从顶部开始
② 在树形结构中向左下移动,直至达到左节点(再也没有子节点的左节点);首先执行此节点。

③ 查看此节点(首先执行的节点)有无同级节点,也就是弟节点;有则执行弟节点。
9有其同级弟节点11,所以执行11。
④ 执行完这两个节点,再向上执行它们的父节点。
执行完11后,9与11的父节点是8,所以执行8。
此时执行顺序是 10->9->11->8
⑤ 完成这一组父子节点后,在树形结构中,向上退一级,查看上退后这一组的父子节点,先左子节点,再右子节点,最后父节点。
按照前文描述规律依次执行。
需要注意的是14优先于13执行,因为14是13的子节点。如果某节点有子节点,则先执行子节点;如果有两个同级子节点,则先执行左子节点。
此时的执行顺序是 10->9->11->8->12->7->14->13->6
⑥ 在树形结构中依照此逻辑不断上移,直至根节点。
最终的执行顺序是 10->9->11->8->12->7->14->13->6->15->5->4->3->2->1->0
4.实战技巧
在工作中进行SQL优化时,分析执行计划肯定不可能像前文一样查父节点、深度、position,然后再画个树形结构图。没有这么多时间,客户也没耐心等。前文的描述都只是为了加深理解。
在工作中应该怎么快速理清楚执行计划顺序呢?
分享一种我经常用的方法。
(1)利用截图工具的箭头找到执行计划入口

(2)看入口步骤有没有弟节点,再看弟节点有没有子节点
如果有弟节点,弟节点无子节点,那么第二步就是该弟节点。
如果有弟节点,弟节点有子节点,那么第二步就是从该弟节点开始用第Ⅰ步中箭头方式,找到它的没有子节点的节点。
像我们这个执行计划,入口没有弟节点。那么,就往上找它的父节点,即9,第二步执行9。
(3)拉长箭头找对应的兄弟节点
找到执行计划的入口后,没有弟节点,就往上走(上为父)。根据前文描述:先弟节点,弟节点执行完了,执行父节点。
且执行计划用肉眼直接去看兄弟节点,很容易看错的。所以可以借助截图工具中的箭头拉长,看两个步骤是否有相同的缩进。比对后相同缩进的步骤就是兄弟节点,上为兄,下为弟;兄先于弟执行。
对于这个例子而言,执行完9,通过箭头发现,11是它的弟节点,所以下一步执行11。
执行完11,执行9和11的父节点8,执行完8,通过箭头发现12是8的弟节点,所以下一步执行12。
以此类推,就可以得出执行顺序了。
10->9->11->8->12->7->14->13->6->15->5->4->3->2->1->0

这种方法适合刚开始接触执行计划的新手DBA使用,如果经常看执行计划,这种方法很快也可以抛弃了,因为肉眼就可以大致判断出执行计划顺序了。




