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