Oracle性能優化,組合索引查詢

在Oracle中能夠建立組合索引,即同時包含兩個或兩個以上列的索引。ide

組合索引的使用存在着必定的侷限,只有在謂詞中出現所有索引列時才能使用效率最高的index unique scan, 不然謂詞中必須包含前導列,不然會走Index full scan或者FTS。測試

    一、 當使用基於規則的優化器(RBO)時,只有當組合索引的前導列出如今SQL語句的where子句中時,纔會使用到該索引;
    二、 在使用Oracle9i以前的基於成本的優化器(CBO)時,只有當組合索引的前導列出如今SQL語句的where子句中時,纔可能會使用到該索引,這取決於優化器計算的使用索引的成本和使用全表掃描的成本,Oracle會自動選擇成本低的訪問路徑;
    三、 從Oracle9i起,Oracle引入了一種新的索引掃描方式——索引跳躍掃描(index skip scan),這種掃描方式只有基於成本的優化器(CBO)才能使用。這樣,當SQL語句的where子句中即便沒有組合索引的前導列,而且索引跳躍掃描的成本低於其餘掃描方式的成本時,Oracle就會使用該方式掃描組合索引;
    四、 Oracle優化器有時會作出錯誤的選擇,由於它再「聰明」,也不如咱們SQL語句編寫人員更清楚表中數據的分佈,在這種狀況下,經過使用提示(hint),咱們能夠幫助Oracle優化器做出更好的選擇。 優化

QL> 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

 當使用基於規則的優化器(RBO)時,只有當組合索引的前導列出如今SQL語句的where子句中時,纔會使用到該索引;code

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 也不必定很聰明。orm

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會選擇什麼樣的訪問路徑呢?ip

看下面的測試:get

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 的優化器。hash

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明智地選擇了全表掃描而不是索引掃描。io

下面,讓咱們來看看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優化器做出更好的選擇。

相關文章
相關標籤/搜索