1. 程式人生 > >[Oracle] 獲取運行計劃的各方法總結

[Oracle] 獲取運行計劃的各方法總結

mit statistic 另一個 waiting 長時間 trac process one ext

總的結論:

一.獲取運行計劃的6種方法(具體步驟已經在每一個樣例的開頭凝視部分說明了):
1. explain plan for獲取;
2. set autotrace on 。
3. statistics_level=all;
4. 通過dbms_xplan.display_cursor輸入sql_id參數直接獲取
5. 10046 trace跟蹤
6. awrsqrpt.sql

二.適用場合分析

1.假設某SQL運行很長時間才會出結果。甚至慢到返回不了結果,這時候看運行計劃就僅僅能用方法1,或者方法4調用現成的。
2.跟蹤某條SQL最簡單的方法是方法1,其次就是方法2。

3.假設想觀察到某條SQL有多條運行計劃的情況。僅僅能用方法4和方法6;
4.假設SQL中含有多函數,函數中套有SQL等多層遞歸調用,想準確分析。僅僅能用法5。
5.要想確保看到真實的運行計劃,不能用方法1和方法2;

6.要想獲取表被訪問的次數。僅僅能用法3;


環境構造

--研究Nested Loops Join訪問次數前準備工作

DROP TABLE t1 CASCADE CONSTRAINTS PURGE; 
DROP TABLE t2 CASCADE CONSTRAINTS PURGE; 
CREATE TABLE t1 (
     id NUMBER NOT NULL,
     n NUMBER,
     contents VARCHAR2(4000)
   )
   ; 
CREATE TABLE t2 (
     id NUMBER NOT NULL,
     t1_id NUMBER NOT NULL,
     n NUMBER,
     contents VARCHAR2(4000)
   )
   ; 
execute dbms_random.seed(0); 
INSERT INTO t1
     SELECT  rownum,  rownum, dbms_random.string(‘a‘, 50)
       FROM dual
     CONNECT BY level <= 1000
      ORDER BY dbms_random.random; 
INSERT INTO t2 SELECT rownum, rownum, rownum, dbms_random.string(‘b‘, 50) FROM dual CONNECT BY level <= 100000
    ORDER BY dbms_random.random; 
COMMIT; 
CREATE INDEX t1_n ON t1 (n);
CREATE INDEX t2_t1_id ON t2(t1_id);

以下我們將會用多種方法來查看例如以下語句的運行計劃
SELECT *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);

方法1(explain plan for 的方式。

類似PLSQL DEVELOPE裏的F5)

步驟1:explain plan for "你的SQL"
步驟2:select * from table(dbms_xplan.display());

set linesize 1000
set pagesize 2000
explain plan for
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);
select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash value: 3532430033
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                  |          |       |       |            |          |
|   2 |   NESTED LOOPS                 |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   3 |    INLIST ITERATOR             |          |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |     2 |  4056 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |     1 |  2041 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("T1"."N"=18 OR "T1"."N"=19)
   6 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)

已選擇24行。

長處: 1.無需真正運行。快捷方便
缺陷: 1.沒有輸出執行時的相關統計信息(產生多少邏輯讀,多少次遞歸調用,多少次物理讀的情況)。
2.無法推斷是處理了多少行;
3.無法推斷表被訪問了多少次。
確實啊,這畢竟都沒有真正執行又怎樣得知真實執行產生的統計信息。

方法2(set autotrace on 方式)

步驟1:set autotrace on
步驟2:在此處運行你的SQL就可以,興許自然會有結果輸出

另,有例如以下幾種方式:
set autotrace on (得到執行計劃,輸出執行結果)
set autotrace traceonly (得到執行計劃,不輸出執行結果)
set autotrace traceonly explain (得到運行計劃,不輸出運行結果和統計信息部分,僅展現運行計劃部分)
set autotrace traceonl statistics(不輸出執行結果和執行計劃部分,僅展現統計信息部分)
set autotrace on 
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);

運行計劃
----------------------------------------------------------
Plan hash value: 3532430033
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                  |          |       |       |            |          |
|   2 |   NESTED LOOPS                 |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   3 |    INLIST ITERATOR             |          |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |     2 |  4056 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |     1 |  2041 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("T1"."N"=18 OR "T1"."N"=19)
   6 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)
統計信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         12  consistent gets
          0  physical reads
          0  redo size
       1032  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

長處:1.能夠輸出執行時的相關統計信息(產生多少邏輯讀,多少次遞歸調用,多少次物理讀的情況);
2.盡管必需要等語句運行完成後才幹夠輸出運行計劃。可是能夠有traceonly開關來控制返回結果不打屏輸出。



缺陷:1.必需要等到語句真正運行完成後。才幹夠出結果;
2.無法看到表被訪問了多少次。

方法3(statistics level=all的方式)

步驟1:alter session set statistics_level=all ;
步驟2:在此處運行你的SQL
步驟3:select * from table(dbms_xplan.display_cursor(null,null,‘allstats last‘));

另註:

1. 假設你用 /*+ gather_plan_statistics */的方法,能夠省略步驟1,直接步驟2,3。
2. keyword解讀(當中OMem、1Mem和User-Mem在興許的課程中會陸續見到):
Starts為該sql運行的次數。
E-Rows為運行計劃估計的行數。
A-Rows為實際返回的行數。A-Rows跟E-Rows做比較。就能夠確定哪一步運行計劃出了問題。
A-Time為每一步實際運行的時間(HH:MM:SS.FF),依據這一行能夠知道該sql耗時在了哪個地方。
Buffers為每一步實際運行的邏輯讀或一致性讀。
Reads為物理讀。
OMem:當前操作完畢全部內存工作區(Work Aera)操作所總共使用私有內存(PGA)中工作區的大小,
這個數據是由優化器統計數據以及前一次運行的性能數據估算得出的
1Mem:當工作區大小無法滿足操作所需的大小時,須要將部分數據寫入暫時磁盤空間中(假設僅須要寫入一次就能夠完畢操作,
就稱一次通過,One-Pass;否則為多次通過,Multi_Pass).該列數據為語句最後一次運行中,單次寫磁盤所須要的內存
大小,這個由優化器統計數據以及前一次運行的性能數據估算得出的
User-Mem:語句最後一次運行中,當前操作所使用的內存工作區大小,括號中面為(發生磁盤交換的次數,1次即為One-Pass,
大於1次則為Multi_Pass,假設沒有使用磁盤,則顯示OPTIMAL)
OMem、1Mem為運行所需的內存評估值,0Mem為最優運行模式所需內存的評估值。1Mem為one-pass模式所需內存的評估值。
0/1/M 為最優/one-pass/multipass運行的次數。Used-Mem耗的內存

set autotrace off          
alter session set statistics_level=all ;
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);
select * from table(dbms_xplan.display_cursor(null,null,‘allstats last‘));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------
SQL_ID  1a914ws3ggfsn, child number 0
-------------------------------------
SELECT  * FROM t1, t2 WHERE t1.id = t2.t1_id AND t1.n in(18,19)

Plan hash value: 3532430033
-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |      1 |        |      2 |00:00:00.01 |      12 |
|   1 |  NESTED LOOPS                  |          |      1 |        |      2 |00:00:00.01 |      12 |
|   2 |   NESTED LOOPS                 |          |      1 |      2 |      2 |00:00:00.01 |      10 |
|   3 |    INLIST ITERATOR             |          |      1 |        |      2 |00:00:00.01 |       5 |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |      2 |      2 |      2 |00:00:00.01 |       5 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |      2 |      1 |      2 |00:00:00.01 |       3 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |      2 |      1 |      2 |00:00:00.01 |       5 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |      2 |      1 |      2 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access(("T1"."N"=18 OR "T1"."N"=19))
   6 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)

已選擇29行。

長處:1.能夠清晰的從STARTS得出表被訪問多少。
2.能夠清晰的從E-ROWS和A-ROWS中得到預測的行數和真實的行數。從而能夠準確推斷Oracle評估是否準確。


3.盡管沒有專門的輸出執行時的相關統計信息,可是執行計劃中的BUFFERS就是真實的邏輯讀的多少

缺陷:1.必需要等到語句真正運行完成後。才幹夠出結果。
2.無法控制記錄輸屏打出。不像autotrace有 traceonly 能夠控制不將結果打屏輸出。
3.看不出遞歸調用的次數。看不出物理讀的多少(只是邏輯讀才是重點)

方法4(知道sql_id後。直接帶入的方式,簡單,就步驟1)


步驟1: select * from table(dbms_xplan.display_cursor(‘&sq_id‘)); (該方法是從共享池裏得到)


註:
1. 另一個方法,select * from table(dbms_xplan.display_awr(‘&sq_id‘));(這是awr性能視圖裏獲取到的)
2. 假設有多運行計劃,能夠用類似方法查出
select * from table(dbms_xplan.display_cursor(‘cyzznbykb509s‘,0));
select * from table(dbms_xplan.display_cursor(‘cyzznbykb509s‘,1));

select * from table(dbms_xplan.display_cursor(‘1a914ws3ggfsn‘));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID  1a914ws3ggfsn, child number 0
-------------------------------------
SELECT  * FROM t1, t2 WHERE t1.id = t2.t1_id AND t1.n in(18,19)

Plan hash value: 3532430033
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |       |       |     6 (100)|          |
|   1 |  NESTED LOOPS                  |          |       |       |            |          |
|   2 |   NESTED LOOPS                 |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   3 |    INLIST ITERATOR             |          |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |     2 |  4056 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |     1 |  2041 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access(("T1"."N"=18 OR "T1"."N"=19))
   6 - access("T1"."ID"="T2"."T1_ID")

Note
-----
   - dynamic sampling used for this statement (level=2)

長處:1.知道sql_id馬上可得到運行計劃。和explain plan for 一樣無需運行;
2.能夠得到真實的運行計劃。(停。等等,啥真實的,剛才這幾個套路中,還有假的運行計劃的嗎?)


缺陷: 1.沒有輸出執行時的相關統計信息(產生多少邏輯讀,多少次遞歸調用,多少次物理讀的情況);
2.無法推斷是處理了多少行;
3.無法推斷表被訪問了多少次。

方法5(10046TRACE)

步驟1:alter session set events ‘10046 trace name context forever,level 12‘; (開啟跟蹤)
步驟2:運行你的語句
步驟3:alter session set events ‘10046 trace name context off‘; (關閉跟蹤)
步驟4:找到跟蹤後產生的文件
步驟5:tkprof trc文件 目標文件 sys=no sort=prsela,exeela,fchela (格式化命令)

set autotace off
alter session set statistics_level=typical;     
alter session set events ‘10046 trace name context  forever,level 12‘;

SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);   
   
alter session set events ‘10046 trace name context off‘;   
select d.value
|| ‘/‘
|| LOWER (RTRIM(i.INSTANCE, CHR(0)))
|| ‘_ora_‘
|| p.spid
|| ‘.trc‘ trace_file_name
from (select p.spid
      from v$mystat m,v$session s, v$process p
      where  m.statistic#=1 and s.sid=m.sid and p.addr=s.paddr) p,
      (select t.INSTANCE
       FROM v$thread t,v$parameter v
       WHERE v.name=‘thread‘
       AND(v.VALUE=0 OR t.thread#=to_number(v.value))) i,
       (select value
       from v$parameter
       where name=‘user_dump_dest‘) d;

exit      
 
tkprof d:\oracle\diag\rdbms\test11g\test11g\trace/test11g_ora_2492.trc    d:\10046.txt  sys=no sort=prsela,exeela,fchela       

SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19)

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0         12          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0         12          0           2

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 94  

Rows     Row Source Operation
-------  ---------------------------------------------------
      2  NESTED LOOPS  (cr=12 pr=0 pw=0 time=0 us)
      2   NESTED LOOPS  (cr=10 pr=0 pw=0 time=48 us cost=6 size=8138 card=2)
      2    INLIST ITERATOR  (cr=5 pr=0 pw=0 time=16 us)
      2     TABLE ACCESS BY INDEX ROWID T1 (cr=5 pr=0 pw=0 time=0 us cost=2 size=4056 card=2)
      2      INDEX RANGE SCAN T1_N (cr=3 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 108621)
      2    INDEX RANGE SCAN T2_T1_ID (cr=5 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 108622)
      2   TABLE ACCESS BY INDEX ROWID T2 (cr=2 pr=0 pw=0 time=0 us cost=2 size=2041 card=1)


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     

長處:1.能夠看出SQL語句相應的等待事件
2.假設SQL語句中有函數調用,SQL中有SQL,將會都被列出,無處遁形。
3.能夠方便的看出處理的行數,產生的物理邏輯讀。
4.能夠方便的看出解析時間和運行時間。
5.能夠跟蹤整個程序包

缺陷: 1.步驟繁瑣。比較麻煩
2.無法推斷表被訪問了多少次。
3.運行計劃中的謂詞部分不能清晰的展現出來。


方法6. awrsqrpt.sql

步驟1:@?/rdbms/admin/awrsqrpt.sql
步驟2:選擇你要的斷點(begin snap 和end snap)
步驟3:輸入你的sql_id


[Oracle] 獲取運行計劃的各方法總結