oracle查看执行计划

转自http://blog.itpub.net/26736162/viewspace-2136865/

一般来说,有如下几种获取执行计划的方式:

1、AUTOTRACE方式

AUTOTRACE是Oracle自带的客户端工具SQLPlus的一个特性。启用AUTOTRACE后,SQLPlus会自动收集执行过的SQL语句的执行计划、性能统计数据等,并在语句执行结束后显示在SQL*Plus中。

DBA用户可以直接使用AUTOTRACE功能,但是如果用户没有DBA权限,那么需要在SYS用户下执行plustrce.sql脚本,自动创建PLUSTRACE角色,再把PLUSTRACE权限赋给普通用户即可。

$ORACLE_HOME/sqlplus/admin/plustrce.sql

GRANT PLUSTRACE TO USER_LHR;

另外,若启用AUTOTRACE报“SP2-0611”的错误,则可以执行utlxplan.sql脚本来创建表PLAN_TABLE,如下所示:

SQL> set autot on

SP2-0613: 无法验证 PLAN_TABLE 格式或实体

SP2-0611: 启用EXPLAIN报告时出错

SQL> @?/rdbms/admin/utlxplan.sql

在执行如下脚本后,每个用户(包括以后新建的用户)都可以使用AUTOTRACE命令:

@?/rdbms/admin/utlxplan.sql

CREATE PUBLIC SYNONYM PLAN_TABLE FOR PLAN_TABLE;

GRANT ALL ON PLAN_TABLE TO PUBLIC;

@?/sqlplus/admin/plustrce.sql

GRANT PLUSTRACE TO PUBLIC;

AUTOTRACE的语法如下所示:

SET AUTOTRACE {OFF|ON|TRACEONLY} [EXPLAIN] [STATISTICS]

其中,AUTOTRACE可简写为AUTOT,TRACEONLY可简写为TRACE,EXPLAIN可简写为EXP,STATISTICS可简写为STAT。

SQL> SET AUTOT ON

SQL> SELECT COUNT(*) FROM PLAN_TABLE;

COUNT(*)


68

Execution Plan


Plan hash value: 1751138260


| Id | Operation | Name | Rows | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | 1 | 3 (0)| 00:00:01 |

| 1 | SORT AGGREGATE | | 1 | | |

| 2 | TABLE ACCESS FULL| PLAN_TABLE$ | 68 | 3 (0)| 00:00:01 |


Note


  • dynamic sampling used for this statement

Statistics


27 recursive calls

0 db block gets

15 consistent gets

0 physical reads

0 redo size

515 bytes sent via SQL*Net to client

487 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed

AUTOTRACE STATISTICS含义见下表:

序号

列名

解释

1

recursive calls

递归调用,表示执行SQL的时候的产生的递归调用的次数。Oracle在执行SQL的时候,有时候会生成很多额外的SQL语句,这个就称为递归调用。这个参数和访问数据字典的次数有很大的关系,一般来说,这个参数值不会很大。

2

db block gets

DB块取,表示当前读。在发生INSERT、DELETE、UPDATE和SELECT FOR UPDATE的时候,数据库缓冲区中的数据库块的个数。在SELECT语句中一般为0。

3

consistent gets

一致性读,表示除了SELECT FOR UPDATE的时候,从数据库缓冲区中读取的数据块的个数(注意,实际上并不是块的个数),可能会读取回滚段的信息,一般来说,逻辑读(Logical Reads) = 当前读(db block gets) + 一致性读(consistent gets)。

4

physical reads

物理读,在执行SQL的过程中,从硬盘上读取的数据块个数。

5

redo size

SQL语句在执行过程中产生的Redo的字节数。

6

bytes sent via SQL*Net to client

服务器利用SQL*Net发送到客户端的字节数。

7

bytes received via SQL*Net from client

服务器利用SQL*Net从客户端接收的字节数。

8

SQL*Net roundtrips to/from client

从客户端发送和接收的SQL*Net消息的总数,包括从多行的结果集中提取的往返消息。

9

sorts (memory)

在内存执行的排序次数。

10

sorts (disk)

在磁盘上执行的排序次数,如果内存空间不足,那么会使用磁盘空间。

11

rows processed

更改或选择返回的行数。

2、EXPLAIN PLAN FOR方式

SQL> EXPLAIN PLAN FOR SELECT * FROM T017_LHRO;

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

PLAN_TABLE_OUTPUT


Plan hash value: 3200443156


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | 1363 | 177K| 9 (0)| 00:00:01 |

| 1 | TABLE ACCESS FULL| T017_LHRO | 1363 | 177K| 9 (0)| 00:00:01 |


3、DBMS_XPLAN.DISPLAY_CURSOR方式

SYS@RAC2LHR1> SELECT * FROM V$VERSION WHERE ROWNUM<2;

BANNER


Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

SYS@RAC2LHR1> SELECT ENAME,DNAME,LOC

2 FROM SCOTT.EMP E,SCOTT.DEPT D

3 WHERE E.DEPTNO = D.DEPTNO

4 AND E.EMPNO = 7788;

ENAME DNAME LOC


SCOTT RESEARCH DALLAS

如果不传递任何参数给DISPLAY_CURSOR函数,那么默认显示当前会话最后一条SQL语句的执行计划,如下所示:

SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL));

PLAN_TABLE_OUTPUT


SQL_ID 315xan8zgvtbm, child number 0


SELECT ENAME,DNAME,LOC FROM SCOTT.EMP E,SCOTT.DEPT D WHERE E.DEPTNO =

D.DEPTNO AND E.EMPNO = 7788

Plan hash value: 1674520956


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | | | 2 (100)| |

| 1 | NESTED LOOPS | | 1 | 33 | 2 (0)| 00:00:01 |

| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 13 | 1 (0)| 00:00:01 |

|* 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |

| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 20 | 1 (0)| 00:00:01 |

|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| |


Predicate Information (identified by operation id):


3 - access("E"."EMPNO"=7788)

5 - access("E"."DEPTNO"="D"."DEPTNO")

24 rows selected.

传递SQL_ID以及FORMAT参数给DISPLAY_CURSOR函数,并配合修饰符控制执行计划的输出,如下所示:

SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('315XAN8ZGVTBM',NULL,'ALL'));

PLAN_TABLE_OUTPUT


SQL_ID 315xan8zgvtbm, child number 0


SELECT ENAME,DNAME,LOC FROM SCOTT.EMP E,SCOTT.DEPT D WHERE E.DEPTNO =

D.DEPTNO AND E.EMPNO = 7788

Plan hash value: 1674520956


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | | | 2 (100)| |

| 1 | NESTED LOOPS | | 1 | 33 | 2 (0)| 00:00:01 |

| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 13 | 1 (0)| 00:00:01 |

|* 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |

| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 20 | 1 (0)| 00:00:01 |

|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| |


Query Block Name / Object Alias (identified by operation id):


1 - SEL$1

2 - SEL$1 / E@SEL$1

3 - SEL$1 / E@SEL$1

4 - SEL$1 / D@SEL$1

5 - SEL$1 / D@SEL$1

Predicate Information (identified by operation id):


3 - access("E"."EMPNO"=7788)

5 - access("E"."DEPTNO"="D"."DEPTNO")

Column Projection Information (identified by operation id):


1 - "ENAME"[VARCHAR2,10], "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]

2 - "ENAME"[VARCHAR2,10], "E"."DEPTNO"[NUMBER,22]

3 - "E".ROWID[ROWID,10]

4 - "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]

5 - "D".ROWID[ROWID,10]

42 rows selected.

利用STATISTICS_LEVEL或/+ GATHER_PLAN_STATISTICS/可以知道表访问的次数,也可以查看真实执行计划并获得统计信息。如下所示:

SET SERVEROUTPUT OFF

ALTER SESSION SET STATISTICS_LEVEL=ALL;

执行SQL语句

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'ADVANCED ALLSTATS LAST'));

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(SQL_ID =>'',CURSOR_CHILD_NO =>1,FORMAT => 'ADVANCED ALLSTATS'));

其中参数SQL_ID为父游标,如果为NULL,那么表示显示该会话之前的SQL执行计划。CURSOR_CHILD_NO为子游标的序号,默认为0,如果设定为NULL,那么所有该父游标下所有的子游标的执行计划都将返回。参数FORMAT指定要显示哪些信息,常用的有:IOSTATS(I/O信息显示)、ALLSTATS(I/O信息显示+PGA信息)、ADVANCED(显示所有统计信息)、IOSTATS LAST或ALLSTATS LAST(只显示最后一次执行的统计信息)。默认值TYPICAL只能显示一个普通的执行计划,不能显示出实际返回的行。

? 这种方式也是SQL调优中常用的方法,但使用该方法的前提是如下两个条件必须同时满足:

① 一般在会话级别设置参数STATISTICS_LEVEL为ALL,也可以使用/+ GATHER_PLAN_STATISTICS/提示。

② 若DBMS_XPLAN.DISPLAY_CURSOR中的入参SQL_ID输入值为NULL的话,则SERVEROUTPUT必须设置为OFF(SET SERVEROUTPUT OFF),否则会报类似如下的错误:

PLAN_TABLE_OUTPUT


SQL_ID 9m7787camwh4m, child number 0

begin :id := sys.dbms_transaction.local_transaction_id; end;

NOTE: cannot fetch plan for SQL_ID: 9m7787camwh4m, CHILD_NUMBER: 0

Please verify value of SQL_ID and CHILD_NUMBER;

It could also be that the plan is no longer in cursor cache (check v$sql_plan)

若为具体SQL_ID的值的话,则无论SERVEROUTPUT的值如何都可以正常执行。

示例如下所示:

SYS@RAC2LHR1> SHOW PARAMETER STATISTICS_LEVEL

NAME TYPE VALUE


statistics_level string TYPICAL

SYS@RAC2LHR1> ALTER SESSION SET STATISTICS_LEVEL=ALL;

Session altered.

SYS@RAC2LHR1> SHOW SERVEROUTPUT

serveroutput OFF

SYS@RAC2LHR1> SELECT ENAME,DNAME,LOC

2 FROM SCOTT.EMP E,SCOTT.DEPT D

3 WHERE E.DEPTNO = D.DEPTNO

4 AND E.EMPNO = 7369;

ENAME DNAME LOC


SMITH RESEARCH DALLAS

SYS@RAC2LHR1> SET PAGESIZE 0

SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'IOSTATS LAST -PREDICATE -NOTE'));

SQL_ID g3mx9hdyrhus7, child number 0


SELECT ENAME,DNAME,LOC FROM SCOTT.EMP E,SCOTT.DEPT D WHERE E.DEPTNO =

D.DEPTNO AND E.EMPNO = 7369

Plan hash value: 1674520956


| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |


| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 |

| 1 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 4 |

| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 |

| 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 |

| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 1 | 1 |00:00:00.01 | 2 |

| 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | 1 | 1 |00:00:00.01 | 1 |


SYS@RAC2LHR1> SELECT /*+ GATHER_PLAN_STATISTICS */ ENAME,DNAME,LOC

2 FROM SCOTT.EMP E,SCOTT.DEPT D

3 WHERE E.DEPTNO = D.DEPTNO

4 AND E.EMPNO = 7369;

SMITH RESEARCH DALLAS

SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'BASIC LAST ALLSTATS'));

EXPLAINED SQL STATEMENT:


SELECT /*+ GATHER_PLAN_STATISTICS */ ENAME,DNAME,LOC FROM SCOTT.EMP

E,SCOTT.DEPT D WHERE E.DEPTNO = D.DEPTNO AND E.EMPNO = 7369

Plan hash value: 1674520956


| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |


| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 |

| 1 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 4 |

| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 |

|* 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 |

| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 1 | 1 |00:00:00.01 | 2 |

|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | 1 | 1 |00:00:00.01 | 1 |


Predicate Information (identified by operation id):


3 - access("E"."EMPNO"=7369)

5 - access("E"."DEPTNO"="D"."DEPTNO")

4、其它跟踪方法

除了上述方法外,还可以通过其它一些途径获取到语句的执行计划,例如10046,10053事件等,但在这些方法所产生的数据里,执行计划通常仅是辅助解决问题的一个部分,而非重点。

5、第三方工具

利用第三方工具,如PL/SQL DEV、TODO等开发工具,在PL/SQL DEV中选定SQL后,按F5即可查看执行计划:

此外,还可以通过写脚本从V$SQL_PLAN、DBA_HIST_SQL_PLAN、V$SQL_PLAN_MONITOR等视图中来获取执行计划。

下表对这几种获取执行计划的方法给予总结:

方法

简介

SQL语句是否真实执行过

是否真实执行计划

物理读、逻辑读、递归调用

运行时间

处理行数

表访问次数

等待事件

解析时间

set autotrace

SET AUTOTRACE OFF

此为默认值,即关闭AUTOTRACE

SET AUTOTRACE ON

包含SQL语句的执行结果、SQL语句执行结果的数量、执行计划和统计信息内容

是

不确定

有

有

有

无

无

无

SET AUTOTRACE ON EXPLAIN

包含SQL语句的执行结果、SQL语句执行结果的数量和执行计划

无

有

有

无

无

无

SET AUTOTRACE ON STATISTICS

包含SQL语句的执行结果、SQL语句执行结果的数量和统计信息内容

有

有

有

无

无

无

SET AUTOTRACE TRACEONLY

包含SQL执行结果的数量、执行计划和统计信息内容,但不显示SQL语句的执行结果

有

有

有

无

无

无

SET AUTOTRACE TRACEONLY EXPLAIN

同EXPLAIN PLAN命令,对于SELECT语句不会执行,只显示目标SQL的执行计划,但是对于DML语句还是会执行的,而且显示SQL语句执行结果的数量和执行计划

否

无

有

有

无

无

无

SET AUTOTRACE TRACEONLY STATISTICS

显示SQL语句执行结果的数量和统计信息,不显示执行计划和SQL执行结果

是

有

有

有

无

无

无

DBMS_XPLAN

EXPLAIN PLAN FOR

DBMS_XPLAN.DISPLAY

EXPLAIN PLAN FOR SQL语句;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(NULL,NULL,'ADVANCED'));

否

不确定

无

无

无

无

无

无

STATISTICS_LEVEL=ALL

SELECT /+ GATHER_PLAN_STATISTICS/ ...

DBMS_XPLAN.DISPLAY_CURSOR

ALTER SESSION SET STATISTICS_LEVEL=ALL ;

执行SQL

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'ALLSTATS LAST'));

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&SQLID',0,'BASIC LAST ALLSTATS ADVANCED'));

是

是

无

有

有

有

无

无

DBMS_XPLAN.DISPLAY_CURSOR

没有设置STATISTICS_LEVEL=ALL或没有使用/+ GATHER_PLAN_STATISTICS/的Hint:

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&SQLID')); --从内存得到执行计划

是

是

无

无

无

无

无

无

DBMS_XPLAN.DISPLAY_AWR

DISPLAY_AWR函数显示存储在AWR历史数据的执行计划。SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&SQLID'));

是

是

无

无

无

无

无

无

DBMS_XPLAN.DISPLAY_SQLSET

DISPLAY_SQLSET函数显示存储在一个SQL调优集中的语句的执行计划,SQL调优集查询DBA_SQLSET_STATEMENTS,查询执行计划的SQL语句为:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_SQLSET('&SQLSET','&SQLID',NULL,'BASIC ALLSTATS ADVANCED'));

是

是

无

无

无

无

无

无

DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE

DISPLAY_SQL_PLAN_BASELINE函数显示存储在数据字典当中SQL执行计划基线的计划。执行计划基线所属SQL的句柄名称(SQL_HANDLE)可以通过视图DBA_SQL_PLAN_BASELINES查询,查询执行计划的SQL语句为:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(SQL_HANDLE => ));

是

是

无

无

无

无

无

无

SQL_TRACE、事件10046、10053跟踪

ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER,LEVEL 12';

执行SQL语句

ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT OFF';

TKPROF格式化TRACE文件

是

是

有

有

有

无

有

有

awrsqrpt.sql

@?/rdbms/admin/awrsqrpt.sql

SELECT OUTPUT FROM TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_SQL_REPORT_HTML(V_DBID,V_INST_ID,V_MIN_SNAP_ID,V_MAX_SNAP_ID,V_SQLID));

是

是

有

有

有

无

有

有

SQL实时监控特性:DBMS_SQLTUNE.REPORT_SQL_MONITOR

SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR('&SQLID') FROM DUAL ;

SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'&SQLID',TYPE=>'ACTIVE',REPORT_LEVEL=>'ALL') AS REPORT FROM DUAL;

是

是

有

有

有

有

有

有

其它工具

利用第三方工具,如PL/SQL DEV、TODO等开发工具,在PL/SQL DEV中选定SQL后,按F5即可查看执行计划

否

不确定

无

无

无

无

无

无

对于这几种获取执行计划的方法有如下结论:

① 若目标SQL需要执行很长时间才能返回结果,则推荐使用EXPLAIN PLAN FOR来获取执行计划。

② 若要查询目标SQL的所有子游标的执行计划,则推荐使用DBMS_XPLAN.DISPLAY_CURSOR('&SQLID', NULL,'ADVANCED ALLSTATS')或awrsqrpt.sql来获取执行计划。

③ 若要分析SQL语句的内部调用详情,则推荐使用10046事件。

④ 若想确保看到真实的执行计划,则不能使用EXPLAIN PLAN FOR和SET AUTOTRACE TRACEONLY EXPLAIN。

⑤ 若想获取到表的访问次数,则推荐/+ GATHER_PLAN_STATISTICS/。

⑥ 若数据库版本大于10g,则对执行时间较长的SQL语句推荐使用SQL实时监控特性查看html报告。

查看执行计划常用方法

1)explain plan命令

2)DBMS_XPLAN包

3)AUTOTRACE开关

4)10046事件

5)10053事件

6)AWR SQL报告
7)PL/SQL工具直接F5

1、 explain plan(相当于PL/SQL的F5)

explain plan for select * from dual;

select * from table(dbms_xplan.display);

注:相关表plan_table$是一个on commit preserve rows的global temporary table。

2、 DBMS_XPLAN包

方法1:配合explain plan使用

explain plan for select * from dual;

select * from table(dbms_xplan.display);

方法2:跟在执行语句后面,‘advanced’比‘all’多显示了“Outline Data”内容

set linesize 800

set pagesize 900

col plan_table_output for a200

select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

方法3:只要目标SQL的执行计划所在的Child Cursor还没有被age out出Shared Pool,就可以使用该方法查看SQL执行计划

select sql_text, sql_id, hash_value, child_number

from v$sql

where sql_text like 'select count(*) from sh.customers%';

select * from table(dbms_xplan.display_cursor('sql_id/hash_value',

child_cursor_number,'advanced'));

方法4:用于查看指定SQL的所有历史执行计划,没有谓词信息

--目标SQL可能有多个Child Cursor,即多个执行计划

select sql_text, sql_id, version_count, executions

from v$sqlarea

where sql_text like 'select count(*) from sh.customers%';

select * from table(dbms_xplan.display_awr('sql_id'));

注:Oracle把执行计划采样数据从V$sql_plan搬到AWR Repository基表wrh$_sql_plan中没有保留谓词信息的记录。

3、 AUTOTRACE开关

可以额外观察到目标SQL执行时所耗费的物理读、逻辑读、产生redo数量以及排序的数量。(statistics)

set outotrace on;

set outotrace off;

set outotrace traceonly;--不现实SQL执行结果

set outotrace traceonly explain;

set outotrace traceonly statistics;

4、 10046事件与tkprof命令

明确显示了目标SQL实际执行计划中每一个执行步骤所消耗的逻辑读、物理读和花费的时间。(在USER_DUMP_DEST目录下生成trace文件。)

--打开

alter session set events '10046 trace name context forever,level 12';

oradebug event 10046 trace name context forever,level 12;

--关闭

alter session set events '10046 trace name context off';

oradebug event 10046 trace name context off;

注:level 12表示trace文件中还包含目标SQL所使用的绑定变量的值以及该session所经历的等待事件。

--操作步骤--

a、SQL> oradebug setmypid;

b、SQL> oradebug event 10046 trace name context forever,level 12;

c、SQL> select count(*) from dual;

d、SQL> oradebug tracefile_name;

/oracle/app/oracle/diag/rdbms/test/test/trace/test_ora_4288.trc

e、SQL> oradebug event 10046 trace name context off;

f、[oracle@test ~]$tkprof /oracle/app/oracle/diag/rdbms/test

/test/trace/test_ora_4288.trc /oracle/test_ora_4288_tkprof.trc

5、 10053事件

6、 AWR SQL报告、Statspack报告

7、 一些现成的脚本(如display_cursor_9i.sql)

8、 PL/SQL工具直接F5

原文地址:http://blog.itpub.net/17203031/viewspace-704626

SQL调优是很多Oracle DBA和开发人员的重要工作。一个高效的SQL改写调优,可以大幅度优化执行计划,提高执行效率,进而增强关键用例模块的可用性和满意度。

进行SQL调优中不可缺少的操作就是获取指定SQL的执行计划。在目前的Oracle版本中,有很多可以使用的执行计划获取方法。本篇就加以总结,供需要的朋友不时之需。

1、方便易用的explain plan

Explain plan命令在Oracle中,可以对后面的SQL语句进行直接的解析,将执行计划保存在一个plan_table的中间表中。之后通过dbms_xplan包的方法进行获取。

ü 确定plan_table的安装

使用explain plan命令的一个前提就是系统中存在plan_table数据表。如果没有的话,需要进行脚本调用安装。

--如果不存在,就生成

SQL> @?/rdbms/admin/catplan.sql

程序包体已创建。

没有错误。

这里注意两个细节:

首先,调用脚本中的?表示ORACLE_HOME目录。如果是使用sqlplus工具,可以直接使用?代指该目录。其他如PL/SQL Developer第三方工具不支持;

其次,如果是Oracle 10g以上的版本,使用脚本名称为catplan.sql。如果是如9i的版本,使用脚本名称为utlxplan.sql。如果在高版本Oracle上使用低版本的plan_table结构,可能在生成执行计划中报错“Plan Table version too old”错误。

ü 使用explain plan for命令生成执行计划并显示

SQL> set linesize 10000;

SQL> set wrap off;

SQL> set pagesize 10000;

SQL> explain plan for select * from scott.emp where empno=7839;

已解释。

之后使用dbms_xplan工具包将生成的执行计划展示出。

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT


Plan hash value: 2949544139


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | 1 | 35 | 1 (0)| 00:00:01 |

| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |

|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |


Predicate Information (identified by operation id):


2 - access("EMPNO"=7839)

已选择14行。

该语句显示出索引执行计划。

ü 显示详细执行计划信息

上面直接调用,是显示出分析的SQL最简单的执行计划。可以通过设置format参数,显示出关于计划的更详细信息。

SQL> select * from table(dbms_xplan.display(null,null,'advanced'));

PLAN_TABLE_OUTPUT


Plan hash value: 2949544139


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | 1 | 35 | 1 (0)| 00:00:01 |

| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |

|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |


Query Block Name / Object Alias (identified by operation id):


1 - SEL$1 / EMP@SEL$1

2 - SEL$1 / EMP@SEL$1

Outline Data


/*+

BEGIN_OUTLINE_DATA

INDEX(@"SEL$1" "EMP"@"SEL$1" ("EMP"."EMPNO"))

OUTLINE_LEAF(@"SEL$1")

ALL_ROWS

OPTIMIZER_FEATURES_ENABLE('10.2.0.1')

IGNORE_OPTIM_EMBEDDED_HINTS

END_OUTLINE_DATA

*/

Predicate Information (identified by operation id):


2 - access("EMPNO"=7839)

Column Projection Information (identified by operation id):


1 - "EMPNO"[NUMBER,22], "EMP"."ENAME"[VARCHAR2,10],

"EMP"."JOB"[VARCHAR2,9], "EMP"."MGR"[NUMBER,22], "EMP"."HIREDATE"[DATE,7],

"EMP"."SAL"[NUMBER,22], "EMP"."COMM"[NUMBER,22], "EMP"."DEPTNO"[NUMBER,22]

2 - "EMP".ROWID[ROWID,10], "EMPNO"[NUMBER,22]

已选择41行。

添加了format参数,Oracle将更加详细的执行计划信息返回,包括Outline信息、结果集合映射等内容。

ü Explain plan for细节

Explain plan for使用比较方便,特别是可以支持在pl/sql developer等第三方开发工具中使用的特性,比较吸引人。不过,explain plan在使用的时候,要注意一些潜在问题:

首先,explain plan for是单纯对SQL语句进行优化器分析,获取产生到的执行计划。这个过程中,并没有真正执行。所以,生成的执行计划有时候会有bug,而且进行统计的信息情况没有autotrace高;

其次,explain plan for由于只是对执行计划进行估计。所以在有绑定变量的SQL时,生成的执行计划并不准确;

2、 获取“刚刚”的执行计划display_cursor

使用dbms_xplan包,还可以获取刚刚执行过的SQL执行计划信息。

SQL> select * from scott.emp where empno=7900;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO


7900 JAMES CLERK 7698 03-12月-81 950 30

SQL> select * from table(dbms_xplan.display_cursor); //获取刚刚的执行计划;

PLAN_TABLE_OUTPUT


SQL_ID 66nkfdw21rc9j, child number 0


select * from scott.emp where empno=7900

Plan hash value: 2949544139


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | | | 1 (100)| |

| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |

|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |


Predicate Information (identified by operation id):


2 - access("EMPNO"=7900)

已选择19行。

直接调用display_cursor,不指定sql_id,就可以将刚刚当前会话执行的SQL命令执行计划从library cache中抽取出来。

注意:display_cursor也支持format参数,可以进行详细执行计划信息的抽取。

此外还有一点,就是这种方法获取刚刚执行过的SQL执行计划,只能在sqlplus或者sqlplusw上使用。如果是pl/sql developer等第三方工具,可能不适用。

(注意:在pl/sql developer下使用存在问题)

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT


SQL_ID 9m7787camwh4m, child number 0

begin :id := sys.dbms_transaction.local_transaction_id; end;

NOTE: cannot fetch plan for SQL_ID: 9m7787camwh4m, CHILD_NUMBER: 0

Please verify value of SQL_ID and CHILD_NUMBER;

It could also be that the plan is no longer in cursor cache (check v$sql_p

8 rows selected

3、autotrace工具使用

本人以为autotrace工具是获取执行计划信息较为完整的工具。优势在于使用该工具可以获取到执行SQL过程中的读写、调用递归和排序分组消耗。

在之前的Blog中,笔者已经撰写过一篇关于autotrace较为详细的文章,有兴趣的读者可以参考:《Autotrace工具使用——小工具,大用场》( http://space.itpub.net/17203031/viewspace-686535 )。

在下篇中,我们会介绍直接从shared_pool中抽取执行计划,和从AWR报告库中抽取。最后介绍使用10046事件跟踪执行计划。

上篇中,我们介绍了几种获取执行计划的方法。本篇我们继续探讨其他获取到执行计划详细信息的方法。

4、从shared_pool中直接抽取执行计划

我们执行过的SQL,在Oracle中会将执行计划缓存一段时间,就在shared_pool的library cache中。这是真实使用的执行计划,我们可以使用手段加以抽取展现。

在shared_pool中,执行计划主要是以shared cursor方式进行保存,也就是父子游标方式。一个父游标parent cursor联动若干child cursor,每个child cursor对应一个单独的执行计划。

SQL> select /*+ exp_demo / from scott.emp where empno=7323;

未选定行

从v$sql和v$sqlarea中获取到对应的计划。

//从v$sqlarea中获取到父游标;

SQL> select substr(sql_text,1,20), sql_id, address, version_count,executions from v$sqlarea where sql_text like 'select /*+ exp_demo /%';

SUBSTR(SQL_TEXT,1,20) SQL_ID ADDRESS VERSION_COUNT EXECUTIONS


select /*+ exp_demo a78616x8uja32 2254266C 1 1

//从v$sql中获取到子游标;

SQL> select sql_id, child_number, executions from v$sql where sql_id='a78616x8uja32';

SQL_ID CHILD_NUMBER EXECUTIONS


a78616x8uja32 0 1

获取到sql_id和child_number之后,就可以使用dbms_xplay.display_cursor方法进行抽取。

SQL> select * from table(dbms_xplan.display_cursor('a78616x8uja32',0));

PLAN_TABLE_OUTPUT


SQL_ID a78616x8uja32, child number 0


select /*+ exp_demo / from scott.emp where empno=7323

Plan hash value: 2949544139


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | | | 1 (100)| |

| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |

|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |


Predicate Information (identified by operation id):


2 - access("EMPNO"=7323)

已选择19行。

这种方式获取到的执行计划是最准确的执行计划。同样display_cursor也是支持format参数。当使用绑定变量时,还可以抽取出bind peeking的变量取值。

5、从AWR报告库中获取执行计划

直接从shared_pool中获取执行计划,虽然是最准确的但存在实效的问题。如果执行一段时间之后,执行计划shared cursor会由于LRU算法被剔除shared_pool。或者因为环境变化,让执行计划重新生成。所以,很多时候,我们需要更多时间进行SQL分析。

这时候我们就需要AWR(Automatic Workload Repository)的镜像snapshot功能。每个固定时间,Oracle AWR会将系统状况已快照的方式保存下来。这个过程中,也就会将这些shared pool执行计划保存下来。

我们通常使用AWR报告时,发现问题SQL的情况。如下:

我们发现sql_id=’ 4x74bc7r4npq4’的SQL存在执行时间长的问题。此时,该SQL可能已经被置换出SGA,所以可以使用dbms_xplan的display_awr方法抽取AWR存储获取执行计划。

SQL> select * from table(dbms_xplan.display_awr('4x74bc7r4npq4',format => 'advanced'));

PLAN_TABLE_OUTPUT


SQL_ID 4x74bc7r4npq4


select ticket0_.SEQ_NUMBER as SEQ1_324_, ticket0_.VERSION as

VERSION324_, ticket0_.CREATE_DATE as CREATE3_324_, ticket0_.CREATE_USER

(篇幅原因,有删节……)

ticket0_.WEB_SALE_I as WEB121_324_ from BSD_TICKET ticket0_ where

TDNR=:1 and TACN=:2

Plan hash value: 3282229029


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | | | 11382 (100)| |

| 1 | TABLE ACCESS FULL| BSD_TICKET | 1 | 582 | 11382 (1)| 00:02:17 |


Query Block Name / Object Alias (identified by operation id):


1 - SEL$1 / TICKET0_@SEL$1

Outline Data


/*+

BEGIN_OUTLINE_DATA

IGNORE_OPTIM_EMBEDDED_HINTS

OPTIMIZER_FEATURES_ENABLE('11.2.0.1')

DB_VERSION('11.2.0.1')

ALL_ROWS

OUTLINE_LEAF(@"SEL$1")

FULL(@"SEL$1" "TICKET0_"@"SEL$1")

END_OUTLINE_DATA

*/

Peeked Binds (identified by position): //绑定变量时用的bind peeking值;


1 - :1 (VARCHAR2(30), CSID=873): '1661663695'

2 - :2 (VARCHAR2(30), CSID=873): '618'

97 rows selected

6、使用10046事件跟踪

传统获取执行计划的方法,是使用10046跟踪事件。通过开启事件跟踪,生成跟踪trace文件。最后通过分析跟踪文件,定位到真实的执行计划。分别按照如下步骤完成:

ü 开启10046跟踪事件,执行诊断SQL

SQL> alter session set events='10046 trace name context forever, level 12';

Session altered

SQL> select * from scott.emp where empno=7323;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO


ü 定位跟踪文件

由于使用的是Oracle 10g,笔者使用自定义的函数进行获取。

SQL> select f_get_trace_name from dual;

F_GET_TRACE_NAME


C:\TOOL\ORACLE\ORACLE\PRODUCT\10.2.0\ADMIN\OTS\UDUMP\ots_ora_3388.trc

说明:如果是在Oracle 11g,可以检索视图v$diag_info来获取当前会话的诊断文件名称。

ü 使用tkprof工具进行跟踪文件处理

由于.trc文件大都是粗格式文档,不宜于阅读。所以可以使用tkprof工具对跟踪文件进行处理。

D:>tkprof ots_ora_3388.trc result.txt

TKPROF: Release 10.2.0.1.0 - Production on 星期三 8月 10 10:04:34 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.

从处理结果文件result.txt中,我们可以找到对应SQL的执行计划信息。

select *

from

scott.emp where empno=7323

call count cpu elapsed disk query current rows


Parse 1 0.03 0.02 0 0 0 0

Execute 1 0.00 0.00 0 0 0 0

Fetch 1 0.00 0.00 0 1 0 0


total 3 0.03 0.02 0 1 0 0

Misses in library cache during parse: 1

Optimizer mode: ALL_ROWS

Parsing user id: SYS

Rows Row Source Operation


0 TABLE ACCESS BY INDEX ROWID EMP (cr=1 pr=0 pw=0 time=45 us)

0 INDEX UNIQUE SCAN PK_EMP (cr=1 pr=0 pw=0 time=30 us)(object id 51152)

Elapsed times include waiting on following events:

Event waited on Times Max. Wait Total Waited

---------------------------------------- Waited ---------- ------------

SQL*Net message to client 2 0.00 0.00

SQL*Net message from client 2 0.03 0.03

该种方法比较复杂,但是获取到的信息很精确。同时,也可以获取到关于SQL处理三阶段(Parse、Execute和Fetch)的相应处理内容。

7、结论

SQL执行计划是我们研究Oracle、研究Oracle优化器的一个重要手段工具。本篇系列关注如何获取SQL的执行计划,列举出六种详细的手段和方法。不同方法均有其优缺点和适应环境,选择正确的方法才可以起到最好的效果。

posted @ 2022-01-07 10:29  良辰好景虚设  阅读(1536)  评论(0)    收藏  举报