【oracle 效能優化】組合索引查詢。

楊奇龍發表於2010-11-19
在Oracle中可以建立組合索引,即同時包含兩個或兩個以上列的索引組合索引的使用存在著一定的侷限,只有在謂詞中出現全部索引列時才能使用效率最高的index unique scan, 否則謂詞中必須包含前導列,否則會走Index full scan或者FTS。
SQL> create index idx_test on yangtest (object_type,object_name);
索引已建立。
SQL> exec dbms_stats.gather_table_stats(user,'YANGTEST',cascade=>true);
PL/SQL 過程已成功完成。
已用時間:  00: 00: 20.78
SQL> select object_type,count(*) from yangtest group by object_type order by 2;

OBJECT_TYPE           COUNT(*)
------------------- ---------- 
EDITION                      1
RULE                         1
MATERIALIZED VIEW            1 
SCHEDULE                     2 
WINDOW GROUP                 4
DIRECTORY                    5 
UNDEFINED                    6 
LOB PARTITION                7 
RESOURCE PLAN                7 
CONTEXT                      7 
WINDOW                       9
CLUSTER                     10
JOB                         11 
EVALUATION CONTEXT          11 
INDEXTYPE                   11
JOB CLASS                   13
CONSUMER GROUP              14
RULE SET                    17
PROGRAM                     18
QUEUE                       33
OPERATOR                    57
XML SCHEMA                  91
TABLE PARTITION            108 
INDEX PARTITION            128
PROCEDURE                  131
LIBRARY                    179
TYPE BODY                  224
SEQUENCE                   227 
FUNCTION                   296
JAVA DATA                  324
TRIGGER                    482
LOB                        760 
JAVA RESOURCE              833 
PACKAGE BODY              1206
PACKAGE                   1267
TABLE                     2543 
TYPE                      2616
INDEX                     3194 
VIEW                      4749 
JAVA CLASS               22103 
SYNONYM                  26670 

已選擇41行。
已用時間:  00: 00: 00.09
1、當使用基於規則的優化器(RBO)時,只有當組合索引的前導列出現在SQL語句的where子句中時,才會使用到該索引;
SQL> set autot trace
SQL> select /*+ rule */ * from yangtest where object_type='JOB';
已選擇11行。
已用時間:  00: 00: 00.07
執行計劃
----------------------------------------------------------
Plan hash value: 2067289980 
------------------------------------------------ 
| Id  | Operation                   | Name     | 
------------------------------------------------ 
|   0 | SELECT STATEMENT            |          |
|   1 |  TABLE ACCESS BY INDEX ROWID| YANGTEST |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST |
------------------------------------------------ 
Predicate Information (identified by operation id):
--------------------------------------------------- 
   2 - access("OBJECT_TYPE"='JOB')
Note       
-----      
   - rule based optimizer used (consider using cbo)
統計資訊
----------------------------------------------------------
          1  recursive calls
          0  db block gets 
         13  consistent gets
          0  physical reads 
          0  redo size   
       2310  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) 
         11  rows processed 
-- 而讓CBO自己選擇時,卻選擇了TFS,從資訊統計裡面可以看出consistent gets 是前者的100倍。CBO 也不一定很聰明。
SQL> select * from yangtest where object_type='JOB';
已選擇11行。
已用時間:  00: 00: 00.03
執行計劃
---------------------------------------------------------- 
Plan hash value: 911235955 
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     | 
------------------------------------------------------------------------------ 
|   0 | SELECT STATEMENT  |          |  1668 |   164K|   275   (1)| 00:00:04 | 
|*  1 |  TABLE ACCESS FULL| YANGTEST |  1668 |   164K|   275   (1)| 00:00:04 | 
------------------------------------------------------------------------------
Predicate Information (identified by operation id):  
---------------------------------------------------  
   1 - filter("OBJECT_TYPE"='JOB')  
統計資訊
---------------------------------------------------------- 
        264  recursive calls     
          0  db block gets   
       1050  consistent gets   
          0  physical reads    
          0  redo size        
       2006  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) 
         11  rows processed
由於使用了組合索引的前導列並且訪問了表中的少量記錄,Oracle明智地選擇了索引掃描。那麼,如果我們訪問表中的大量資料時,Oracle會選擇什麼樣的訪問路徑呢?看下面的測試:
SQL> select * from yangtest where object_type='SYNONYM';
已選擇26670行。
已用時間:  00: 00: 01.42
執行計劃
----------------------------------------------------------
Plan hash value: 911235955
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |  1668 |   164K|   275   (1)| 00:00:04 | 
|*  1 |  TABLE ACCESS FULL| YANGTEST |  1668 |   164K|   275   (1)| 00:00:04 | 
------------------------------------------------------------------------------ 
Predicate Information (identified by operation id):  
--------------------------------------------------- 
   1 - filter("OBJECT_TYPE"='SYNONYM')
統計資訊
---------------------------------------------------------- 
          1  recursive calls 
          0  db block gets  
       2769  consistent gets 
          0  physical reads  
          0  redo size 
    1228701  bytes sent via SQL*Net to client 
      19963  bytes received via SQL*Net from client 
       1779  SQL*Net roundtrips to/from client 
          0  sorts (memory)     
          0  sorts (disk)    
      26670  rows processed 
測試一下是使用RULE 的優化器。
SQL> select /*+ rule */ * from yangtest where object_type='SYNONYM';
已選擇26670行。
已用時間:  00: 00: 01.56
執行計劃
----------------------------------------------------------                               

                    
Plan hash value: 2067289980 
------------------------------------------------ 
| Id  | Operation                   | Name     | 
------------------------------------------------ 
|   0 | SELECT STATEMENT            |          |
|   1 |  TABLE ACCESS BY INDEX ROWID| YANGTEST |
|*  2 |   INDEX RANGE SCAN          | IDX_TEST |
------------------------------------------------
Predicate Information (identified by operation id):
--------------------------------------------------- 
   2 - access("OBJECT_TYPE"='SYNONYM')
Note       
-----      
   - rule based optimizer used (consider using cbo)
統計資訊
----------------------------------------------------------
          1  recursive calls  
          0  db block gets  
      23543  consistent gets --明顯比cbo的執行計劃的多10倍。
          0  physical reads  
          0  redo size      
    3235078  bytes sent via SQL*Net to client   
      19963  bytes received via SQL*Net from client
       1779  SQL*Net roundtrips to/from client   
          0  sorts (memory)    
          0  sorts (disk)    
      26670  rows processed  
  從以上結果可以看出,在訪問大量資料的情況下,使用索引確實會導致更高的執行成本,這從statistics部分的邏輯讀取數(consistent gets)就可以看出,使用索引導致的邏輯讀取數是不使用索引導致的邏輯讀的10倍還多。因此,Oracle明智地選擇了全表掃描而不是索引掃描。
下面,讓我們來看看where子句中沒有索引前導列的情況:
SQL> select * from yangtest where object_name ='EMP';
已用時間:  00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 4208055961
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     2 |   202 |    45   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| YANGTEST |     2 |   202 |    45   (0)| 00:00:01 | 
|*  2 |   INDEX SKIP SCAN           | IDX_TEST |     2 |       |    43   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):   
--------------------------------------------------- 
   2 - access("OBJECT_NAME"='EMP') 
       filter("OBJECT_NAME"='EMP') 
統計資訊
---------------------------------------------------------- 
          1  recursive calls  
          0  db block gets  
         35  consistent gets 
          1  physical reads 
          0  redo size    
       1337  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)     
          1  rows processed  

SQL> select * from yangtest where object_name ='YANGTEST';
未選定行
已用時間:  00: 00: 00.01
執行計劃
----------------------------------------------------------
Plan hash value: 4208055961 
---------------------------------------------------------------------------------------- 
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     | 
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     2 |   202 |    45   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| YANGTEST |     2 |   202 |    45   (0)| 00:00:01 |
|*  2 |   INDEX SKIP SCAN           | IDX_TEST |     2 |       |    43   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id): 
--------------------------------------------------- 
   2 - access("OBJECT_NAME"='YANGTEST')  
       filter("OBJECT_NAME"='YANGTEST')  
統計資訊
---------------------------------------------------------- 
          1  recursive calls   
          0  db block gets     
         27  consistent gets   
          0  physical reads     
          0  redo size         
       1124  bytes sent via SQL*Net to client
        405  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)      
          0  sorts (disk)       
          0  rows processed    

 沒有使用前導列,Oracle正確地選擇了索引跳躍掃描。我們再來看看如果不使用索引跳躍掃描,該語句的成本:
SQL> select /*+ NO_INDEX(YANGTEST,IDX_TEST)*/ * from yangtest where object_name ='DEPT';
已用時間:  00: 00: 00.01
執行計劃
---------------------------------------------------------- 
Plan hash value: 911235955     
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     2 |   202 |   275   (1)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| YANGTEST |     2 |   202 |   275   (1)| 00:00:04 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):  
---------------------------------------------------
   1 - filter("OBJECT_NAME"='DEPT') 
統計資訊
---------------------------------------------------------- 
          1  recursive calls    
          0  db block gets     
       1011  consistent gets  --是使用索引跳躍掃描的50倍左右
          0  physical reads     
          0  redo size         
       1335  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)      
          1  rows processed     

SQL> select * from yangtest where object_name like 'T%';
已選擇136行。
已用時間:  00: 00: 00.04
執行計劃
----------------------------------------------------------
Plan hash value: 911235955     
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |   925 | 93425 |   275   (1)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| YANGTEST |   925 | 93425 |   275   (1)| 00:00:04 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id): 
--------------------------------------------------- 
   1 - filter("OBJECT_NAME" LIKE 'T%') 
統計資訊
----------------------------------------------------------  
          1  recursive calls   
          0  db block gets     
       1020  consistent gets    
          0  physical reads    
          0  redo size         
       8900  bytes sent via SQL*Net to client 
        515  bytes received via SQL*Net from client 
         11  SQL*Net roundtrips to/from client  
          0  sorts (memory)    
          0  sorts (disk)      
        136  rows processed 
 這次只選擇了136條資料,跟表YANGTEST中總的資料量29489條相比,顯然只是很小的一部分,但是Oracle還是選擇了全表掃描,有1020 個邏輯讀。這種情況下,如果我們強制使用索引.結果如下
SQL> select /*+ INDEX(YANGTEST,IDX_TEST)*/ * from yangtest where object_name like 'T%';
已選擇136行。
已用時間:  00: 00: 00.06
執行計劃
----------------------------------------------------------
Plan hash value: 972231820     
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |   925 | 93425 |  1084   (1)| 00:00:14 |
|   1 |  TABLE ACCESS BY INDEX ROWID| YANGTEST |   925 | 93425 |  1084   (1)| 00:00:14 |
|*  2 |   INDEX FULL SCAN           | IDX_TEST |   925 |       |   424   (1)| 00:00:06 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id): 
--------------------------------------------------- 
   2 - access("OBJECT_NAME" LIKE 'T%')
       filter("OBJECT_NAME" LIKE 'T%')
統計資訊
---------------------------------------------------------- 
          1  recursive calls    
          0  db block gets     
        537  consistent gets   
          0  physical reads    
          0  redo size         
      14700  bytes sent via SQL*Net to client
        515  bytes received via SQL*Net from client
         11  SQL*Net roundtrips to/from client
          0  sorts (memory)    
          0  sorts (disk)      
        136  rows processed    
通過新增提示(hint),我們強制Oracle使用了索引掃描(index full scan),執行了335個邏輯讀,比使用全表掃描的時候少了一些。
    由此可見,Oracle優化器有時會做出錯誤的選擇,因為它再“聰明”,也不如我們SQL語句編寫人員更清楚表中資料的分佈,在這種情況下,通過使用提示(hint),我們可以幫助Oracle優化器作出更好的選擇。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-678549/,如需轉載,請註明出處,否則將追究法律責任。

相關文章