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的執行計划,列舉出六種詳細的手段和方法。不同方法均有其優缺點和適應環境,選擇正確的方法才可以起到最好的效果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM