Oracle中組合索引的使用詳解
在Oracle中可以建立組合索引,即同時包含兩個或兩個以上列的索引。在組合索引的使用方面,Oracle有以下特點:
1、 當使用基於規則的最佳化器(RBO)時,只有當組合索引的前導列出現在SQL語句的where子句中時,才會使用到該索引;
2、 在使用Oracle9i之前的基於成本的最佳化器(CBO)時, 只有當組合索引的前導列出現在SQL語句的where子句中時,才可能會使用到該索引,這取決於最佳化器計算的使用索引的成本和使用全表掃描的成本,Oracle會自動選擇成本低的訪問路徑(請見下面的測試1和測試2);
3、 從Oracle9i起,Oracle引入了一種新的索引掃描方式——索引跳躍掃描(index skip scan),這種掃描方式只有基於成本的最佳化器(CBO)才能使用。這樣,當SQL語句的where子句中即使沒有組合索引的前導列,並且索引跳躍掃描的成本低於其他掃描方式的成本時,Oracle就會使用該方式掃描組合索引(請見下面的測試3);
4、 Oracle最佳化器有時會做出錯誤的選擇,因為它再“聰明”,也不如我們SQL語句編寫人員更清楚表中資料的分佈,在這種情況下,透過使用提示(hint),我們可以幫助Oracle最佳化器作出更好的選擇(請見下面的測試4)。
關於以上情況,我們分別測試如下:
我們建立測試表T,該表的資料來源於Oracle的資料字典表all_objects,表T的結構如下:
SQL> desc t
---------------------------- --------------- --------------------------------------
名稱 是否為空? 型別
---------------------------- --------------- --------------------
OWNER NOT NULL VARCHAR2(30)
OBJECT_NAME NOT NULL VARCHAR2(30)
SUBOBJECT_NAME VARCHAR2(30)
OBJECT_ID NOT NULL NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(18)
CREATED NOT NULL DATE
LAST_DDL_TIME NOT NULL DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
表中的資料分佈情況如下:
SQL> select object_type,count(*) from t group by object_type;
OBJECT_TYPE COUNT(*)
------------------ ----------
CONSUMER GROUP 20
EVALUATION CONTEXT 10
FUNCTION 360
INDEX 69
LIBRARY 20
LOB 20
OPERATOR 20
PACKAGE 1210
PROCEDURE 130
SYNONYM 16100
TABLE 180
TYPE 2750
VIEW 8600
已選擇13行。
SQL> select count(*) from t;
COUNT(*)
----------
29489
---------------------------- --------------- --------------------------------------
名稱 是否為空? 型別
---------------------------- --------------- --------------------
OWNER NOT NULL VARCHAR2(30)
OBJECT_NAME NOT NULL VARCHAR2(30)
SUBOBJECT_NAME VARCHAR2(30)
OBJECT_ID NOT NULL NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(18)
CREATED NOT NULL DATE
LAST_DDL_TIME NOT NULL DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
表中的資料分佈情況如下:
SQL> select object_type,count(*) from t group by object_type;
OBJECT_TYPE COUNT(*)
------------------ ----------
CONSUMER GROUP 20
EVALUATION CONTEXT 10
FUNCTION 360
INDEX 69
LIBRARY 20
LOB 20
OPERATOR 20
PACKAGE 1210
PROCEDURE 130
SYNONYM 16100
TABLE 180
TYPE 2750
VIEW 8600
已選擇13行。
SQL> select count(*) from t;
COUNT(*)
----------
29489
我們在表T上建立如下索引並對其進行分析:
現在讓我們編寫幾條SQL語句來測試一下Oracle最佳化器對訪問路徑的選擇:
測試1)
SQL> set autotrace traceonly
SQL> SELECT * FROM T WHERE OBJECT_TYPE='LOB';
已選擇20行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=22 Card=20 Bytes=1740)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=22 Card=20 Bytes=1740)
2 1 INDEX (RANGE SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=2 Card=20)
正如我們所期望的,由於使用了組合索引的前導列並且訪問了表中的少量記錄,Oracle明智地選擇了索引掃描。那麼,如果我們訪問表中的大量資料時,Oracle會選擇什麼樣的訪問路徑呢?請看下面的測試:
SQL> SELECT * FROM T WHERE OBJECT_TYPE='LOB';
已選擇20行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=22 Card=20 Bytes=1740)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=22 Card=20 Bytes=1740)
2 1 INDEX (RANGE SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=2 Card=20)
正如我們所期望的,由於使用了組合索引的前導列並且訪問了表中的少量記錄,Oracle明智地選擇了索引掃描。那麼,如果我們訪問表中的大量資料時,Oracle會選擇什麼樣的訪問路徑呢?請看下面的測試:
測試2)
SQL> SELECT * FROM T WHERE OBJECT_TYPE='SYNONYM';
已選擇16100行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=16100 Bytes=1400700)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=38 Card=16100 Bytes=1400700)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1438 consistent gets
13 physical reads
0 redo size
941307 bytes sent via SQL*Net to client
12306 bytes received via SQL*Net from client
1075 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
16100 rows processed
很明顯,即使使用了組合索引的前導列,但是由於訪問了表中的大量資料,Oracle選擇了不使用索引而直接使用全表掃描,因為最佳化器認為全表掃描的成本更低,但事實是不是真的這樣的?我們透過增加提示(hint)來強制它使用索引來看看:
已選擇16100行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=16100 Bytes=1400700)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=38 Card=16100 Bytes=1400700)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1438 consistent gets
13 physical reads
0 redo size
941307 bytes sent via SQL*Net to client
12306 bytes received via SQL*Net from client
1075 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
16100 rows processed
很明顯,即使使用了組合索引的前導列,但是由於訪問了表中的大量資料,Oracle選擇了不使用索引而直接使用全表掃描,因為最佳化器認為全表掃描的成本更低,但事實是不是真的這樣的?我們透過增加提示(hint)來強制它使用索引來看看:
SQL> SELECT/**//*+ INDEX (T INDX_T)*/ * FROM T WHERE OBJECT_TYPE='SYNONYM';
已選擇16100行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=16180 Card=16100 Bytes=1400700)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=16180 Card=16100 Bytes=1400700)
2 1 INDEX (RANGE SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=80 Card=16100)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
17253 consistent gets
16 physical reads
0 redo size
298734 bytes sent via SQL*Net to client
12306 bytes received via SQL*Net from client
1075 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
16100 rows processed
已選擇16100行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=16180 Card=16100 Bytes=1400700)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=16180 Card=16100 Bytes=1400700)
2 1 INDEX (RANGE SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=80 Card=16100)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
17253 consistent gets
16 physical reads
0 redo size
298734 bytes sent via SQL*Net to client
12306 bytes received via SQL*Net from client
1075 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
16100 rows processed
從以上結果可以看出,在訪問大量資料的情況下,使用索引確實會導致更高的執行成本,這從statistics部分的邏輯讀取數(consistent gets)就可以看出,使用索引導致的邏輯讀取數是不使用索引導致的邏輯讀的10倍還多。因此,Oracle明智地選擇了全表掃描而不是索引掃描。
下面,讓我們來看看where子句中沒有索引前導列的情況:
測試3)
SQL> select * from t where object_name= 'DEPT';
已選擇10行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=29 Card=14 Bytes=1218)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=29 Card=14 Bytes=1218)
2 1 INDEX (SKIP SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=14 Card=14)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
24 consistent gets
0 physical reads
0 redo size
1224 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed
已選擇10行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=29 Card=14 Bytes=1218)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=29 Card=14 Bytes=1218)
2 1 INDEX (SKIP SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=14 Card=14)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
24 consistent gets
0 physical reads
0 redo size
1224 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed
OK!由於只查詢了10條資料,即使沒有使用前導列,Oracle正確地選擇了索引跳躍掃描。我們再來看看如果不使用索引跳躍掃描,該語句的成本:
SQL> select/**//*+ NO_INDEX(T INDX_T)*/ * from t where object_name= 'DEPT';
已選擇10行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=14 Bytes=1218)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=38 Card=14 Bytes=1218)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
375 consistent gets
17 physical reads
0 redo size
1224 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed
已選擇10行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=14 Bytes=1218)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=38 Card=14 Bytes=1218)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
375 consistent gets
17 physical reads
0 redo size
1224 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10 rows processed
正如我們所料,不使用索引所導致的邏輯讀(375)確實比使用索引的邏輯讀多(24),達到10倍以上。
繼續我們的測試,現在我們來看看Oracle不選擇使用索引的情況:
測試4)
SQL> select * from t where object_name LIKE 'DE%';
已選擇180行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=37 Bytes=3219)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=38 Card=37 Bytes=3219)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
386 consistent gets
16 physical reads
0 redo size
12614 bytes sent via SQL*Net to client
624 bytes received via SQL*Net from client
13 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
180 rows processed
已選擇180行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=38 Card=37 Bytes=3219)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=38 Card=37 Bytes=3219)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
386 consistent gets
16 physical reads
0 redo size
12614 bytes sent via SQL*Net to client
624 bytes received via SQL*Net from client
13 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
180 rows processed
這次只選擇了180條資料,跟表T中總的資料量29489條相比,顯然只是很小的一部分,但是Oracle還是選擇了全表掃描,有386個邏輯讀。這種情況下,如果我們強制使用索引,情況會怎樣呢?
SQL> select/**//*+ INDEX(T INDX_T)*/ * from t where object_name LIKE 'DE%';
已選擇180行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=182 Card=37 Bytes=3219)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=182 Card=37 Bytes=3219)
2 1 INDEX (FULL SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=144 Card=37)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
335 consistent gets
0 physical reads
0 redo size
4479 bytes sent via SQL*Net to client
624 bytes received via SQL*Net from client
13 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
180 rows processed
已選擇180行。
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=182 Card=37 Bytes=3219)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=182 Card=37 Bytes=3219)
2 1 INDEX (FULL SCAN) OF 'INDX_T' (NON-UNIQUE) (Cost=144 Card=37)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
335 consistent gets
0 physical reads
0 redo size
4479 bytes sent via SQL*Net to client
624 bytes received via SQL*Net from client
13 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
180 rows processed
透過新增提示(hint),我們強制Oracle使用了索引掃描(index full scan),執行了335個邏輯讀,比使用全表掃描的時候少了一些。
由此可見,Oracle最佳化器有時會做出錯誤的選擇,因為它再“聰明”,也不如我們SQL語句編寫人員更清楚表中資料的分佈,在這種情況下,透過使用提示(hint),我們可以幫助Oracle最佳化器作出更好的選擇。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/751371/viewspace-567129/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ORACLE 組合索引 使用分析Oracle索引
- Mysql索引的使用-組合索引+跳躍條件MySql索引
- MongoDB複合索引詳解MongoDB索引
- mysql的組合索引MySql索引
- oracle索引詳解 分割槽索引Oracle索引
- 【oracle 效能優化】組合索引查詢。Oracle優化索引
- Oracle中job的使用詳解Oracle
- Mysql索引的使用 - 組合索引 + 範圍條件的處理MySql索引
- 組合索引的前導列與查詢——ORACLE索引Oracle
- 索引與null(二):組合索引索引Null
- mysql組合索引,abc索引命中MySql索引
- oracle組合索引什麼情況下生效?Oracle索引
- c#組合模式詳解C#模式
- js 組合繼承詳解JS繼承
- 【oracle 效能優化】組合索引之index_ssOracle優化索引Index
- 使用組合索引處理包含空值的查詢索引
- ORACLE中RECORD、VARRAY、TABLE的使用詳解Oracle
- MySQL單列索引和組合索引的區別MySql索引
- Numpy陣列的組合與分割詳解陣列
- LNMP組合詳解與實戰LNMP
- Oracle堆組織表的索引和索引組織表Oracle索引
- 組合索引的選擇原則索引
- MySQL 組合索引不生效MySql索引
- 關於ORACLE組合索引內部儲存淺談Oracle索引
- 詳解oracle使用者建立(中)Oracle
- 索引設計(組合索引適用場景)索引
- 使用者組詳解
- Oracle中的SCN詳解Oracle
- SQLServer索引優化(3):對於建組合索引的要求SQLServer索引優化
- MySQL單列索引和組合索引的區別介紹MySql索引
- oracle 索引組織表Oracle索引
- MySQL學習筆記:組合索引-索引下推MySql筆記索引
- 【INDEX】Oracle分割槽索引技術詳解IndexOracle索引
- 索引詳解索引
- 聯合索引和多個單列索引使用中的索引命中情況及索引建立原則索引
- Oracle中的Hash Join詳解Oracle
- Oracle 索引組織表(IOT)Oracle索引
- 【Mysql】InnoDB 中的聚簇索引、二級索引、聯合索引MySql索引