oracle之如何获取执行计划方法

1、什么是执行计划
为了执行sql语句,Oracle在内部必须实现许多步骤,这些步骤可能是从数据库中物理检索数据行,或者用某种方法来准备数据行等,接着Oracle会按照一定的顺序一次执行这些步骤,最后将其执行结果作为目标sql的最终执行结果返回给用户。Oracle用来执行目标sql语句的这些步骤的组合就被称为执行计划。
 
2、如何查看执行计划
 
2.1、explain plan命令
如果你问习惯于使用pl/sql developer的同学数据库得到一个sql执行计划,他们很可能不假思索的回答,按F5就可以了。实际上pl/sql developer上按了F5后,调用了explain plan命令,将该命令封装到软件中了。
依次执行下面两条命令
SQL> explain plan for select count(*) from emp;
先使用explain plan命令对目标sql做explain,
SQL> select * from table(dbms_xplan.display);
再查看执行计划。
到底explain plan命令做了什么事情?在10G及以上版本,对目标sql执行explain plan命令后,则Oracle就解析目标sql 所产生的执行计划的具体执行步骤写到plan_table$,随后执行的select * from table(dbms_xplan.display);只是从plan_table$中将这些具体执行步骤以格式化的方式显示出来。plan_table$是一个ON COMMIT PRESERVER ROWS的GLOBAL TEMPORARY TABLE。所以这里Oracle可以做到各个session只能看到自己的执行的sql所产生的执行计划,并且各个session往plan_table$写入执行计划的过程互不干扰。
 
2.2、DBMS_XPLAN包
使用DBMS_XPLAN包中方法是Oracle数据库中得到目标SQL的执行计划的第二种方法,针对不同的应用场景,你可以选择四种方法中的一种:
2.2.1方法一、select * from table(DBMS_XPLAN.display);
这需要与explain plan命令配合使用,它用于查看使用explain plan命令后得到的执行计划,上面已介绍过。
 
2.2.2方法二、select * from table(DBMS_XPLAN.display_cursor(null,null,'advanced'));
它用在sqlplus中查看刚刚执行的SQL执行计划,这里针对方法DBMS_XPLAN.display_cursor所传入的第一个和第二个参数的值均为null,第三个参数的值是'advanced',第三个输入参数的值也可以是‘all’,只不过'advanced'后的显示结果比ALL显示更详细些。
 
2.2.3方法三、select * from table(dbma_xplan.display_cursor('sql_id/hash_value',child_cursor_number,'advanced'));它用于查看指定sql的执行计划,这里针对方法所传入的第一个参数的值是指定sql的sql id或者sql hash value,第二个参数的值是要查看的执行计划所在的child cursor number。只要目标sql所对应的child cursor还在library cache中,我们就可以从v$sql中查到目标sql的child cursor的详细信息,包括sql id、sql hash value、chaild cursor number等,
select sql_text,sql_id,hash_value,child_number from v$sql where sql_text like '%%';查询结果找出对应的sql_id和hash value,本质上sql id 和hash value是一回事,他们是可以互相转换的,所以第一个参数两个值都可以用。只要执行计划所在的child cursor还没有被age out出shared pool,就可以用这种方法获取执行计划。
 
2.2.4方法四、select * from table(dbms_xplan.display_awr('sql id'));它用于查看指定sql的所有的历史执行计划,使用2、3能够显示目标sql执行计划的前提条件是该sql的执行计划还在shared pool中,而如果该sql的执行计划已经被age out出shared pool,那么只要该sql的执行计划被Oracle采集到awr repository中,我们就可以使用方法4来查看该sql的所有历史执行计划。
这里需要说明的是,到目前为止,用dbms_xplan.display_awr和之前的dbms_xplan.display_cursor显示的执行计划相比,有一个非常不好的地方,就是用dbms_xplan.display_awr显示执行计划中看不到执行步骤对应的谓词条件,这是非常糟糕的。因为谓词条件对于我们理解执行计划,特别是理解复杂执行计划是不可或缺的。这里的根本原因是Oracle在把执行计划的采样数据从v$sql挪到awr repository的基表wrh$_sql_plan中时没有保留v$sql_plan中记录谓词条件(包括驱动查询条件和过滤查询条件)的列ACCESS_PREDICATES和FILTER_PREDICATES的值,所以不是DBMS_XPLAN.DISPLAY_CURSOR不想显示谓词条件,而是根本就没有谓词条件可供其显示。
 
2.3、autotrace开关
在sqlplus中将autotrace开关打开也能得到目标sql的执行计划,而且除此之外还能得到目标sql在执行时的资源消耗量,即通过设置autotrace开关我们可以额外观察到目标sql执行时所消耗的物理读,逻辑读,产生redo的数量以及排序的数量等。
设置autotrace开发语法如下:
set autotrace OFF|ON|TRACEONLY
[explan]
[statistics]
(1)在sqlplus的当前session中执行命令set autotrace on,可以将autotrace开发完全打开,在这个session中随后执行的所有sql除了显示sql执行结果之外,还会额外显示这个sql对应的执行计划和资源消耗情况。
(2)在sqlplus的当前session执行命令set autotrace off,可以在当前的session中将autotrace开关关闭,在这个session中随后执行的结果只显示执行结果,autotrace开关默认值是OFF。
(3)在sqlplus的当前session中执行命令set autotrace traceonly,可以在当前session中将autotrace开关以不显示sql执行结果的具体内容的方式完全打开。这种方式与st autotrace on的唯一区别就在于,对应traceonly而言,Oracle只会显示sql执行结果的数量,而不会显示直接结果的具体内容。所以适用于那些sql执行结果的具体内容特别长,会连续刷屏的sql,这种情况下我们往往并不关心这些sql的执行结果的具体内容,而只是关心他们的执行计划和资源消耗情况。
(4)在sqlplus的当前session中执行命令set autotrace traceonly explain,可以在挡在session中将autotrace开关以只显示执行技术的方式打开。这种方式与set autotrace traceonly的区别就在于,不会显示目标sql的资源消耗量和执行结果数量,而只显示执行计划。
(5)在sqlplus的当前session中执行命令set autotrace traceonly statistics,可以在当前session中将autotrace开关以只显示sql的资源消耗量的方式打开,这种方式与set autotrace traceonly的唯一区别在于,不显示目标sql的执行计划,而只会显示目标sql执行结果的数量和资源消耗情况。
Oracle有如下简写:
autotrace可以简写成autot
traceonly----trace
explan-------exp
statistics-----stat
按照上述简写规则,如下的写法都是等价的:
SET AUTOTRACE ON==SET AUTOT ON
SET AUTOTRACE OFF ==SET AUTOT OFF
SET AUTOTRACE TRACEONLY ==SET AUTOT TRACE
SET AUTOTRACE TRACEONLY EXPLAIN ==SET AUTOT TRACE EXP
SET AUTOTRACE TRACEONLY STATISTICS ==SET AUTOT TRACE STAT
 
2.4、10046时间和tkprof命令
使用10046事件是在Oracle数据库中查看目标SQL的执行计划的另一种方法,这种方法与使用explain plan命令、DBMS_XPLAN包和AUTOTRACE开关的不同之处在于,所得到的执行计划中明确显示了目标SQL实际执行计划中每一个执行步骤所消耗的逻辑读、物理读和花费的时间。这种细粒度的明显显示在我们诊断复杂SQL的性能问题时尤为有用,而且这也是其他三种方法所不能提供的(实际上,用GATHER_PLAN_STATISTICS Hint配合DBMS_XPLAN包一起使用也可以达到类似10046事件这种细粒度的明细显示效果)。执行10046事件的步骤:
a)首先在当前session中激活10046事件
b)接着在此session中执行目标SQL
c)最后在此session中关闭10046事件
当执行完上述步骤后,Oracle就会将目标SQL的执行计划和明细资源消耗写入此session所对应的trace文件中,查看这个trace文件就能知道目标sql的执行计划和资源消耗明细,Oracle会在参数USER_DUMP_DEST所代表的目录下生产这个trace文件,其命名格式为"实例名_ora_当前session的spid.trc",例如oratest_ora_219.trc。通常可以使用如下这两种方法在当前session中激活10046事件:
在当前session中执行alter session set events '10046 trace name context forever,level 12';
在当前session中执行oradebug event 10046 trace name context forever,level 12
上述命令的关键字level后的数字是表示设置的10046事件的level值,这个值是可以修改的,我们通常使用的值为12,表示在产生的trace文件中除了有目标SQL的执行计划和资源消耗明细之外,还会包含目标SQL所使用的绑定变量的值以及该session所经历的等待事件,除了上述LEVEL值之外,其他部分都是固定的语法。这里我们推荐使用第二种方法,因为可以在激活10046事件后执行命令oradebug tracefile_name来得到当前session所对应的trace文件的具体路径和名称。
对应的,在当前session中关闭10046事件的两种方法为:
在当前session中执行:
alter session set events '10046 trace name context off';
oradebug event 10046 trace name context off;
这里需要注意的是,10046事件所产生的原始trace文件我们习惯称之为裸trace文件(new trace),Oracle记录在裸trace文件中的内容一眼看上去并不是只管,也不是那么容易能看懂,为了让上述裸trace文件能够以一种更直观、更容易懂的方式展现出来,Oracle提供了tkprof命令,这个命令是Oracle自带的,可以用它来翻译上述裸trace文件,从随后的示例中可以看到翻译后的内容确实更直观。
示例:
oradebug setmypid      ----表示对当前session使用oradebug命令
oradebug event 10046 trace name context forever,level 12;     --打开10046事件
select empno from scott.emp;                                                       --目标SQL
oradebug tracefile_name     --显示trace文件路径
oradebug event 10046 trace name context off;                            --关闭10046事件
 
开启另外一个窗口:
tkprof d:\app\administrator\diag\rdbms\ortest\ortest\trace\ortest_ora_7784.trc
output = d:\app\20211213_tkprof.tra
即可查看编译后的文件。
 
3、如何得到真实的执行计划
在上面介绍了四种方法,除了第四种方法外,其他三种方法得到的执行计划都有可能是不准确的。在Oracle数据库中判断得到的执行计划是否准确,就是看目标SQL是否真正执行,真正执行过的SQL所对应的执行计划就是准的,反之则可能不准。注意,这里的判断原则从严格意义上来说并不适用于AUTOTRACE开关,因为所有使用AUTOTRACE开关显示的执行计划都有可能是不准的,即使对应的目标SQL实际上已经执行过,这一点在后会详细解释。
下面我们就用上述原则来判断除了第4中以外的其他三种方法中哪些方法得到的执行计划是准的,哪些是可能不准的。
对使用第一种方法(EXPLAIN PLAN)得到的执行计划而言,因为此时目标SQL并没有实际执行,所以用该方法得到的执行计划有可能是不准的,尤其是在目标SQL包含绑定变量时,在默认开启绑定变量窥探(Bind Peeking)情况下,对含有绑定变量的目标SQL使用explain plan得到的执行计划只是一个半成品,Oracle在随后对该SQL的绑定变量进行窥探后就得到额这些绑定变量具体的值,此时Oracle很可能会对上述半成品的执行计划做调整,一旦做了调整,使用EXPAIN PLAN命令得到的执行计划就不准了。
对于使用第二种方法(DBMS_XPLAN包)而言,针对不同的应用场景,你可以选择以下的一种:
select * from table(DBMS_XPLAN.display);
select * from table(DBMS_XPLAN.display_cursor(null,null,'advanced'));
select * from table(dbma_xplan.display_cursor('sql_id/hash_value',child_cursor_number,'advanced'));
select * from table(dbms_xplan.display_awr('sql id'));
显然select * from table(DBMS_XPLAN.display);所得到的执行计划可能是不准的。因为它知识用于查看使用expain plan命令得到的执行计划,目标sql此时还没有真正执行,所以用它得到的执行计划可能是不准的。使用剩下三种方式所得到的执行计划都是准的。因为此时SQL都已经被实际执行过。
对使用第三种方法(AUTO TRACE开关),可以使用如下三种方式中的一种开启AUTO TRACE开关:
set autotrace on
set autotrace traceonly
set autotrace traceonly explan
上述三种方式中,当使用set autotrace on和set autotrace traceonly时,SQL都已经被实际执行过了,真是因为被实际执行过,所以在此情况下我们能扩大农目标SQL的实际资源消耗情况。当使用set autotrace traceonly explan时,如果执行的是select语句,则该select语句并没有被Oracle实际执行,但如果执行的是DML语句,情况就不一样了,此时的DML语句是会被实际执行的。所以在使用上面三种方法来获得DML语句的执行计划要小心,因为这些DML语句实际上已经被执行了。
这里需要特别注意的是:虽然使用部分SET AUTOTRACE命令后SQL实际上已经被执行了,但是所有使用SET AUTOTRACE命令所得到的执行计划都有可能是不准的,因为autotrace命令所显示的执行计划都是来源于调用expain plan命令。
  

posted on 2021-12-22 20:19  小杜的学习天地  阅读(586)  评论(0编辑  收藏  举报

导航