天天看點

淺談Oracle執行計劃

要對sql語句進行分析,首先就是要去看sql語句的執行計劃是否存在問題,oracle在10g之後,預設采用CBO基于代價的優化器,sql語句的執行計劃根據統計資訊分析來決定,如果統計資訊未收集,則采取動态采樣的方式來決定最優的執行計劃!

一: 擷取sql語句的執行計劃,在使用執行計劃前,先以sys使用者運作腳本$ORACLE_HOME/sqlplus/admin/plustrce.sql,該腳本建立了plustrace角色,并給該角色查詢v$sessstat,v$statname,v$mystat三個動态性能視圖的權限;最後将plustrace角色授予普通使用者

1:建立測試表,填充資料,建立索引

SQL> create table t as select 1 id,object_name from dba_objects;  

Table created.  

SQL> select count(*) from t;  

  COUNT(*)  

----------  

     77262  

SQL> update t set id=99 where rownum=1;  

1 row updated.  

SQL> commit;  

Commit complete.  

SQL> select id,count(*) from t group by id;  

        ID   COUNT(*)  

---------- ----------  

         1      77261  

        99          1  

SQL> create index i_t_id on t(id);  

Index created. 

2:擷取sql語句的執行計劃,‘dynamic sampling used for this statement (level=2)’表示采取級别2的動态采樣;執行計劃的步驟為靠右靠上先執行,而不是第一列的id順序,在本例中先執行縮進最靠右的I_T_ID索引範圍掃描,然後根據索引掃描出來的結果定位到T表相應行的rowid,謂詞中的"2 - access("ID"=99)"表示where後條件id=99會對id為2的INDEX RANGE SCAN造成決定行的影響,這個也很好了解,在本例中如果where語句後面為id=1,則必然選擇全表掃描才是最優的執行計劃;rows則會傳回的結果集行數,統計資訊中對應select語句主要看實體讀和一緻性讀的個數

SQL> set autot traceonly  

SQL> select * from t where id=99;  

Execution Plan  

----------------------------------------------------------  

Plan hash value: 4153437776  

--------------------------------------------------------------------------------------  

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

|   0 | SELECT STATEMENT            |        |     1 |    79 |     2   (0)| 00:00:01 |  

|   1 |  TABLE ACCESS BY INDEX ROWID| T      |     1 |    79 |     2   (0)| 00:00:01 |  

|*  2 |   INDEX RANGE SCAN          | I_T_ID |     1 |       |     1   (0)| 00:00:01 |  

Predicate Information (identified by operation id):  

---------------------------------------------------  

   2 - access("ID"=99)  

Note  

-----  

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

Statistics  

         10  recursive calls  

          0  db block gets  

         67  consistent gets  

          1  physical reads  

          0  redo size  

        491  bytes sent via SQL*Net to client  

        420  bytes received via SQL*Net from client  

          2  SQL*Net roundtrips to/from client  

          0  sorts (memory)  

          0  sorts (disk)  

          1  rows processed 

3:使用dbms_stats包對表進行收集統計資訊,在本例中可以看出進行分析過的表,将不會繼續使用動态采樣的方式收集資訊

<a href="http://blog.51cto.com/attachment/201206/112255933.jpg" target="_blank"></a>

SQL&gt; exec dbms_stats.gather_table_stats('HR','T',CASCADE=&gt;TRUE);  

PL/SQL procedure successfully completed.  

|   0 | SELECT STATEMENT            |        |    14 |   378 |     2   (0)| 00:00:01 |  

|   1 |  TABLE ACCESS BY INDEX ROWID| T      |    14 |   378 |     2   (0)| 00:00:01 |  

|*  2 |   INDEX RANGE SCAN          | I_T_ID |    14 |       |     1   (0)| 00:00:01 |  

          0  recursive calls  

          3  consistent gets  

          0  physical reads  

4:對表分析後的表進行update操作,在未重新進行分析前,将産生錯誤的執行計劃,重新分析後正常;因而在生産環境中,經常有DML操作的表應當根據實際情況進行分析,否則将産生類似的問題

SQL&gt; set autot off  

SQL&gt; update t set id=99 where id=1;  

77261 rows updated.  

SQL&gt; update t set id=1 where rownum=1;  

         1          1  

        99      77261  

77261 rows selected.  

      11017  consistent gets  

      13840  redo size  

    3170997  bytes sent via SQL*Net to client  

      57070  bytes received via SQL*Net from client  

       5152  SQL*Net roundtrips to/from client  

      77261  rows processed  

SQL&gt; select /*+ full(t) */ *  from t where id=99;  

Plan hash value: 1601196873  

--------------------------------------------------------------------------  

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

|   0 | SELECT STATEMENT  |      |    14 |   378 |   101   (1)| 00:00:02 |  

|*  1 |  TABLE ACCESS FULL| T    |    14 |   378 |   101   (1)| 00:00:02 |  

   1 - filter("ID"=99)  

          1  recursive calls  

       5477  consistent gets  

    2357600  bytes sent via SQL*Net to client  

SQL&gt; exec dbms_stats.gather_table_stats('HEROSTART_CN','T',CASCADE=&gt;TRUE);  

|   0 | SELECT STATEMENT  |      | 77241 |  2036K|   101   (1)| 00:00:02 |  

|*  1 |  TABLE ACCESS FULL| T    | 77241 |  2036K|   101   (1)| 00:00:02 |  

      10845  consistent gets  

      77261  rows processed 

5:執行計劃的另一種檢視方法,使用explain,同set autot on方式比(set autot traceonly隻産生執行計劃和統計資訊,不執行實際的sql語句,因而廣泛用于生産環境中),不産生"Statistics"資訊

SQL&gt; explain plan for select * from t where id=1;  

Explained.  

SQL&gt; select * from table(dbms_xplan.display);  

PLAN_TABLE_OUTPUT  

--------------------------------------------------------------------------------  

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

   2 - access("ID"=1)  

14 rows selected. 

二:直方圖(histogram),指資料在列上的值分布情況;傾斜表場合,應當做對索引列做直方圖,否則同樣會産生錯誤的執行計劃

DBMS_STATS包對表進行分析主要包含

1:表本身的分析,包括表的行數,資料塊數,每一行的長度等資訊

2:列的分析,包括列的重複數,列的空值數,列的值分布情況等

3:索引的分析,包括索引的塊數,索引的深度(blevel),索引的聚合因子等

SQL&gt; create table t1 as select  1 id,object_name from dba_objects;  

SQL&gt; update t1 set id=99 where rownum=1;  

SQL&gt; create index i_t1_id on t1(id);  

Index created.  

SQL&gt; select table_name,column_name,endpoint_number,endpoint_value from user_hist  

ograms where table_name='T1';  

no rows selected  

SQL&gt; exec dbms_stats.gather_table_stats('HEROSTART_CN','T1',CASCADE=&gt;TRUE);  

TABLE_NAME           COLUMN_NAME          ENDPOINT_NUMBER ENDPOINT_VALUE  

-------------------- -------------------- --------------- --------------  

T1                   ID                                 0              1  

T1                   OBJECT_NAME                        0     2.4504E+35  

T1                   ID                                 1             99  

T1                   OBJECT_NAME                        1     6.2963E+35  

SQL&gt; select count(*),id from t1 group by id;  

  COUNT(*)         ID  

     77267          1  

         1         99  

SQL&gt; select * from t1 where id=99;  

Plan hash value: 1111474805  

---------------------------------------------------------------------------------------  

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

|   0 | SELECT STATEMENT            |         |    14 |   378 |     2   (0)| 00:00:01 |  

|   1 |  TABLE ACCESS BY INDEX ROWID| T1      |    14 |   378 |     2   (0)| 00:00:01 |  

|*  2 |   INDEX RANGE SCAN          | I_T1_ID |    14 |       |     1   (0)| 00:00:01 |  

        351  consistent gets  

        487  bytes sent via SQL*Net to client  

          1  rows processed  

SQL&gt; select * from t1 where id=1;  

77267 rows selected.  

---------------------------------------------------------  

Plan hash value: 3617692013  

|   0 | SELECT STATEMENT  |      | 77254 |  2036K|   101   (1)| 00:00:02 |  

|*  1 |  TABLE ACCESS FULL| T1   | 77254 |  2036K|   101   (1)| 00:00:02 |  

   1 - filter("ID"=1)  

          8  recursive calls  

       5489  consistent gets  

    2357825  bytes sent via SQL*Net to client  

      57081  bytes received via SQL*Net from client  

       5153  SQL*Net roundtrips to/from client  

          3  sorts (memory)  

      77267  rows processed  

SQL&gt; exec dbms_stats.delete_column_stats('HR','T1','ID');  

|   0 | SELECT STATEMENT            |         |   773 | 20871 |    77   (0)| 00:00:01 |  

|   1 |  TABLE ACCESS BY INDEX ROWID| T1      |   773 | 20871 |    77   (0)| 00:00:01 |  

|*  2 |   INDEX RANGE SCAN          | I_T1_ID |   309 |       |    75   (0)| 00:00:01 |  

      10781  consistent gets  

    3171208  bytes sent via SQL*Net to client  

SQL&gt; exec dbms_stats.gather_table_stats('HR','T1',CASCADE=&gt;TRUE);  

T1                   ID                              5527             99  

T1                   ID                              5526              1  

T1                   OBJECT_NAME                        1     6.2963E+35 

三:動态采樣,動态采樣分0-10,11個級别,級别越高,采樣的結果越精确,需要消耗的資料庫成本也越高

level0:不進行動态采樣

level1:對沒有進行分析的表進行動态采樣,要求同時滿足下列4個條件;

sql語句中至少有一個未分析的表

未分析的表出現在關聯查詢或者子查詢中

未分析的表沒有索引

未分析的表占用的資料塊大于動态采樣預設的資料塊(32個)

level2:對所有未分析的表做分析,動态采樣的資料塊是預設資料塊的2倍

level3:采樣的表包含滿足level2定義的所有表,同時包括,謂詞中包含的潛在的需要動态采樣的表

level4:采樣的表滿足level3定義的所有表,同時還包括一些表,他們包含一個單表的謂詞會引用另外的2個列或者更多的列

level5,6,7,8,9:采樣的表包含滿足level4定義的所有表,同時分别使用預設資料庫的2,4,8,32,128倍的數量做動态采樣

level10:采樣的表滿足level9定義的所有表,同時對表的所有資料進行動态采樣

1:建立基表,未收集統計資訊前,user_tables視圖中的相關資訊未填充,11g版本中,user_indexes視圖中的資訊會被填充

SQL&gt; create table t2 as select object_id,object_name from dba_objects;  

SQL&gt; create index i_t2_id on t2 (object_id);  

SQL&gt; select num_rows,avg_row_len,blocks,last_analyzed from user_tables where tab  

le_name='T2';  

  NUM_ROWS AVG_ROW_LEN     BLOCKS LAST_ANALYZED  

---------- ----------- ---------- ---------------  

SQL&gt; select blevel,leaf_blocks,distinct_keys,num_rows,last_analyzed from user_in  

dexes where table_name='T2';  

    BLEVEL LEAF_BLOCKS DISTINCT_KEYS   NUM_ROWS LAST_ANALYZED  

---------- ----------- ------------- ---------- -------------------  

         1         171         77265      77265 2012-06-07:13:55:04 

2:執行查詢,毫無疑問的采用動态采樣,也能擷取正确的執行計劃,‘1 - filter("OBJECT_ID"&gt;30)’表示對結果進行過濾

SQL&gt; select * from t2 where object_id &gt; 30;  

77236 rows selected.  

Plan hash value: 1513984157  

|   0 | SELECT STATEMENT  |      | 58208 |  4490K|   105   (0)| 00:00:02 |  

|*  1 |  TABLE ACCESS FULL| T2   | 58208 |  4490K|   105   (0)| 00:00:02 |  

   1 - filter("OBJECT_ID"&gt;30)  

         24  recursive calls  

       5586  consistent gets  

          6  physical reads  

    3005346  bytes sent via SQL*Net to client  

      57059  bytes received via SQL*Net from client  

       5151  SQL*Net roundtrips to/from client  

          6  sorts (memory)  

      77236  rows processed 

3:使用hint提示符,禁用動态采樣,将産生錯誤的執行計劃

SQL&gt; select /*+ dynamic_sampling(t2 0) */ * from t2 where object_id &gt; 30;  

Plan hash value: 3661687773  

|   0 | SELECT STATEMENT            |         |  1556 |   120K|     8   (0)| 00:00:01 |  

|   1 |  TABLE ACCESS BY INDEX ROWID| T2      |  1556 |   120K|     8   (0)| 00:00:01 |  

|*  2 |   INDEX RANGE SCAN          | I_T2_ID |   280 |       |     3   (0)| 00:00:01 |  

   2 - access("OBJECT_ID"&gt;30)  

      10897  consistent gets  

        165  physical reads  

    3314234  bytes sent via SQL*Net to client  

3:收集統計資訊後,user_tables視圖相應的内容會被填充,禁用動态采樣也能擷取正确的執行計劃

SQL&gt; exec dbms_stats.gather_table_stats('HR','T2',CASCADE=&gt;TRUE);  

---------- ----------- ---------- -------------------  

     77266          29        381 2012-06-07:14:05:52  

         1         171         77265      77265 2012-06-07:14:05:52  

|   0 | SELECT STATEMENT  |      | 77242 |  2187K|   106   (1)| 00:00:02 |  

|*  1 |  TABLE ACCESS FULL| T2   | 77242 |  2187K|   106   (1)| 00:00:02 |  

         12  recursive calls  

       5516  consistent gets  

          5  sorts (memory)  

本文以《讓oracle跑的更快》為指導,如有雷同,不勝榮幸!

本文轉自斬月部落格51CTO部落格,原文連結http://blog.51cto.com/ylw6006/892178如需轉載請自行聯系原作者

ylw6006