索引掃描(Index scan)
我們先通過index查找到數據對應的rowid值(對於非唯一索引可能返回多個rowid值),然後根據rowid直接從表中得到具體的數據,這種查找方式稱爲索引掃描或索引查找(index lookup)。一個rowid唯一的表示一行數據,該行對應的數據塊是通過一次i/o得到的,在此情況下該次i/o只會讀取一個數據庫塊。在索引中,除了存儲每個索引的值外,索引還存儲具有此值的行對應的ROWID值。索引掃描可以由2步組成: (1) 掃描索引得到對應的rowid值。 (2) 通過找到的rowid從表中讀出具體的數據。
根據索引的類型與where限制條件的不同,有5種類型的索引掃描:
1)索引唯一掃描(index unique scan)
2)索引範圍掃描(index range scan)
3)索引全掃描(index full scan)
4)索引快速掃描(index fast full scan)
5)索引跳躍掃描(INDEX SKIP SCAN)
索引唯一掃描(INDEX UNIQUE SCAN)
通過唯一索引查找一個數值經常返回單個ROWID
唯一索引由單獨列組成:
1 --收集統計信息 2 SQL> exec dbms_stats.gather_table_stats('SCOTT','EMP'); 3 4 PL/SQL procedure successfully completed. 5 6 Commit complete. 7 SQL> 8 9 10 --獲取創建索引語句 11 SQL> SELECT DBMS_METADATA.GET_DDL('INDEX',u.index_name) 12 2 FROM USER_INDEXES u 13 3 WHERE u.TABLE_NAME='EMP'; 14 15 DBMS_METADATA.GET_DDL('INDEX',U.INDEX_NAME) 16 -------------------------------------------------------------------------------- 17 CREATE UNIQUE INDEX "SCOTT"."PK_EMP" ON "SCOTT"."EMP" ("EMPNO") 18 PCTFREE 10 19 20 SQL> 21 22 1.索引名稱 PK_EMP 23 2.索引包含列 EMPNO 24 3.索引爲唯一索引 25 26 --執行計劃走唯一索引的語句 27 SQL> SELECT * FROM SCOTT.EMP WHERE EMPNO='7369'; 28 29 Execution Plan 30 ---------------------------------------------------------- 31 Plan hash value: 2949544139 32 33 -------------------------------------------------------------------------------------- 34 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 35 -------------------------------------------------------------------------------------- 36 | 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 | 37 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 | 38 |* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 | 39 -------------------------------------------------------------------------------------- 40 41 Predicate Information (identified by operation id): 42 --------------------------------------------------- 43 44 2 - access("EMPNO"=7369) 45 46 47 48 SQL> SELECT * FROM SCOTT.EMP WHERE EMPNO IN ('7499','7521'); 49 50 51 Execution Plan 52 ---------------------------------------------------------- 53 Plan hash value: 2355049923 54 55 --------------------------------------------------------------------------------------- 56 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 57 --------------------------------------------------------------------------------------- 58 | 0 | SELECT STATEMENT | | 2 | 76 | 2 (0)| 00:00:01 | 59 | 1 | INLIST ITERATOR | | | | | | 60 | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 76 | 2 (0)| 00:00:01 | 61 |* 3 | INDEX UNIQUE SCAN | PK_EMP | 2 | | 1 (0)| 00:00:01 | 62 --------------------------------------------------------------------------------------- 63 64 Predicate Information (identified by operation id): 65 --------------------------------------------------- 66 67 3 - access("EMPNO"=7499 OR "EMPNO"=7521) 68 69 SQL> SELECT * FROM SCOTT.EMP WHERE EMPNO='7499' OR EMPNO='7521'; 70 71 Execution Plan 72 ---------------------------------------------------------- 73 Plan hash value: 2355049923 74 75 --------------------------------------------------------------------------------------- 76 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 77 --------------------------------------------------------------------------------------- 78 | 0 | SELECT STATEMENT | | 2 | 76 | 2 (0)| 00:00:01 | 79 | 1 | INLIST ITERATOR | | | | | | 80 | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 76 | 2 (0)| 00:00:01 | 81 |* 3 | INDEX UNIQUE SCAN | PK_EMP | 2 | | 1 (0)| 00:00:01 | 82 --------------------------------------------------------------------------------------- 83 84 Predicate Information (identified by operation id): 85 --------------------------------------------------- 86 87 3 - access("EMPNO"=7499 OR "EMPNO"=7521) 88 SQL>
SELECT * FROM SCOTT.EMP WHERE EMPNO='7369';
SELECT * FROM SCOTT.EMP WHERE EMPNO IN ('7499','7521');
SELECT * FROM SCOTT.EMP WHERE EMPNO='7499' OR EMPNO='7521'
總結,索引在where條件中,且謂詞條件可以確定唯一值時,走唯一索引。思考下2,3語句的查詢過程
唯一索引由多個列組成(即組合索引)
1 --創建一個唯一索引(優質索引) 2 create unique index scott.idx_test on scott.emp(ename, deptno); --ename爲引導列,表中ename列值具有唯一性 3 4 --謂詞條件中的列順序與索引的列順序完全一致,走唯一索引 5 SQL> select * from scott.emp where ename = 'ALLEN' and deptno = 20 ; 6 7 no rows selected 8 9 10 Execution Plan 11 ---------------------------------------------------------- 12 Plan hash value: 4010583877 13 14 ---------------------------------------------------------------------------------------- 15 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 16 ---------------------------------------------------------------------------------------- 17 | 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 | 18 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 | 19 |* 2 | INDEX UNIQUE SCAN | IDX_TEST | 1 | | 0 (0)| 00:00:01 | 20 ---------------------------------------------------------------------------------------- 21 22 Predicate Information (identified by operation id): 23 --------------------------------------------------- 24 25 2 - access("ENAME"='ALLEN' AND "DEPTNO"=20) 26 27 28 --謂詞條件中的列順序與唯索引的列順序不一致 ,走唯一索引 29 SQL> select * from scott.emp where deptno = 20 and ename = 'ALLEN'; 30 31 no rows selected 32 33 34 Execution Plan 35 ---------------------------------------------------------- 36 Plan hash value: 4010583877 37 38 ---------------------------------------------------------------------------------------- 39 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 40 ---------------------------------------------------------------------------------------- 41 | 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 | 42 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 | 43 |* 2 | INDEX UNIQUE SCAN | IDX_TEST | 1 | | 0 (0)| 00:00:01 | 44 ---------------------------------------------------------------------------------------- 45 46 Predicate Information (identified by operation id): 47 --------------------------------------------------- 48 49 2 - access("ENAME"='ALLEN' AND "DEPTNO"=20) 50 51 --只有引導列在謂詞條件中 52 SQL> select * from scott.emp where ename = 'ALLEN'; --即使是唯一數據 也不走唯一索引 53 54 55 Execution Plan 56 ---------------------------------------------------------- 57 Plan hash value: 2317538385 58 59 ---------------------------------------------------------------------------------------- 60 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 61 ---------------------------------------------------------------------------------------- 62 | 0 | SELECT STATEMENT | | 1 | 38 | 2 (0)| 00:00:01 | 63 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 2 (0)| 00:00:01 | 64 |* 2 | INDEX RANGE SCAN | IDX_TEST | 1 | | 1 (0)| 00:00:01 | 65 ---------------------------------------------------------------------------------------- 66 67 Predicate Information (identified by operation id): 68 --------------------------------------------------- 69 70 2 - access("ENAME"='ALLEN') 71 72 73 --引導列不在謂詞條件中 74 SQL> select * from scott.emp where deptno = 20; 75 76 77 Execution Plan 78 ---------------------------------------------------------- 79 Plan hash value: 3956160932 80 81 -------------------------------------------------------------------------- 82 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 83 -------------------------------------------------------------------------- 84 | 0 | SELECT STATEMENT | | 5 | 190 | 3 (0)| 00:00:01 | 85 |* 1 | TABLE ACCESS FULL| EMP | 5 | 190 | 3 (0)| 00:00:01 | 86 -------------------------------------------------------------------------- 87 88 Predicate Information (identified by operation id): 89 --------------------------------------------------- 90 91 1 - filter("DEPTNO"=20) 92 93 SQL>
1 --創建一個唯一索引(劣質索引) 2 create unique index idx_test on scott.emp(deptno,ename); --deptno爲引導列,表中deptno列值不具有唯一性 3 4 分別對別如下sql的執行計劃: 5 --謂詞條件中的列順序與索引的列順序完全一致,,走唯一索引 6 SQL> select * from scott.emp where deptno = 20 and ename = 'ALLEN'; 7 8 no rows selected 9 10 Execution Plan 11 ---------------------------------------------------------- 12 Plan hash value: 1531058326 13 14 ------------------------------------------------------------------------------------------ 15 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 16 ------------------------------------------------------------------------------------------ 17 | 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 | 18 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 | 19 |* 2 | INDEX UNIQUE SCAN | IDX_TEST01 | 1 | | 0 (0)| 00:00:01 | 20 ------------------------------------------------------------------------------------------ 21 22 Predicate Information (identified by operation id): 23 --------------------------------------------------- 24 25 2 - access("DEPTNO"=20 AND "ENAME"='ALLEN') 26 27 SQL> 28 29 --謂詞條件中的列順序與唯索引的列順序不一致 ,走唯一索引 30 SQL> select * from scott.emp where ename = 'ALLEN' and deptno = 20 ; 31 32 no rows selected 33 34 Execution Plan 35 ---------------------------------------------------------- 36 Plan hash value: 1531058326 37 38 ------------------------------------------------------------------------------------------ 39 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 40 ------------------------------------------------------------------------------------------ 41 | 0 | SELECT STATEMENT | | 1 | 38 | 1 (0)| 00:00:01 | 42 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 38 | 1 (0)| 00:00:01 | 43 |* 2 | INDEX UNIQUE SCAN | IDX_TEST01 | 1 | | 0 (0)| 00:00:01 | 44 ------------------------------------------------------------------------------------------ 45 46 Predicate Information (identified by operation id): 47 --------------------------------------------------- 48 49 2 - access("DEPTNO"=20 AND "ENAME"='ALLEN') 50 51 SQL> 52 53 --只有引導列在謂詞條件中 54 SQL> select * from scott.emp where deptno = 20; 55 56 Execution Plan 57 ---------------------------------------------------------- 58 Plan hash value: 560737562 59 60 ------------------------------------------------------------------------------------------ 61 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 62 ------------------------------------------------------------------------------------------ 63 | 0 | SELECT STATEMENT | | 5 | 190 | 2 (0)| 00:00:01 | 64 | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | 190 | 2 (0)| 00:00:01 | 65 |* 2 | INDEX RANGE SCAN | IDX_TEST01 | 5 | | 1 (0)| 00:00:01 | 66 ------------------------------------------------------------------------------------------ 67 68 Predicate Information (identified by operation id): 69 --------------------------------------------------- 70 71 2 - access("DEPTNO"=20) 72 SQL> 73 74 --引導列不在謂詞條件 75 SQL> select * from scott.emp where ename = 'ALLEN'; 76 77 Execution Plan 78 ---------------------------------------------------------- 79 Plan hash value: 3956160932 80 81 -------------------------------------------------------------------------- 82 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | 83 -------------------------------------------------------------------------- 84 | 0 | SELECT STATEMENT | | 1 | 38 | 3 (0)| 00:00:01 | 85 |* 1 | TABLE ACCESS FULL| EMP | 1 | 38 | 3 (0)| 00:00:01 | 86 -------------------------------------------------------------------------- 87 88 Predicate Information (identified by operation id): 89 --------------------------------------------------- 90 91 1 - filter("ENAME"='ALLEN') 92 93 SQL>
總結:使用組合索引時,遵守以下原則:
1.引導列標識性要強;
2.索引列儘量全部出現在謂詞條件中
3.引導列儘量出現在謂詞條件中
索引範圍掃描(INDEX RANGE SCAN)
使用一個索引存取多行數據,在唯一索引上使用索引範圍掃描的典型情況下是在謂詞(where限制條件)中使用了範圍操作符 (如>、<、<>、>=、<=、between)。在非唯一索引上,謂詞"="也可能返回多行數據,所以在非唯一索引上都使用索引範圍掃描。
使用index rang scan的3種情況:
1.在唯一索引列上使用了range操作符(> < <> >= <= between)
2.在組合索引上,只使用部分列進行查詢,導致查詢出多行
3.對非唯一索引列上進行的任何查詢。
通過index range scan訪問的表可以通過按照索引順序重新建立表來提高效率:
1.如果你只讀一部分數據,假設20% ,如果表數據順序混亂,實際上可能把整個表都讀進來了; 如果表順序和索引一致,則只需要讀進 20%的表的block就夠了。這是簡單情況 2.複雜情況下,順序混亂的時候 block 可能在整個查詢的不同時間點多次反覆訪問 當再次要訪問這個塊的時候說不定已經被換出去了,或者被修改過了,那代價更大 而如果順序一樣,對同一個block的訪問集中在一段連續的很短的時間內,變數少,不會對同一個block產生多次IO
Index Unique Scan對比Index Range Scan
1.Index Unique Scan和Index Range Scan在B Tree上的搜索路徑是一樣的 2.Index Unique Scan在找到應該含有要找的Index Key的block後便停止了搜索,因爲該鍵是唯一的;而Index Range Scan還要循着指針繼續找下去直到條件不滿足時 3.Index Unique Scan和Index Range Scan都只是索引上的查詢,與是否掃描表沒有關係。 如果所選擇的列都在index上就不用去scan table;如果掃描到表, 必然還有一個table access by rowid
索引全掃描(index full scan)
與全表掃描對應,也有相應的全索引掃描。在某些情況下,可能進行全索引掃描而不是範圍掃描,需要注意的是全索引掃描只在CBO模式下才有效。 CBO根據統計數值得知進行全索引掃描比進行全表掃描更有效時,才進行全索引掃描,而且此時查詢出的數據都必須從索引中可以直接得到。
一般通過索引進行排序時,會用到(index full scan)
索引快速掃描(index fast full scan)
掃描索引中的所有的數據塊,與 index full scan很類似,但是一個顯著的區別就是它不對查詢出的數據進行排序,即數據不是以排序順序被返回。在這種存取方法中,可以使用多塊讀功能,也可以使用並行讀入,以便獲得最大吞吐量與縮短執行時間。
索引跳躍掃描(INDEX SKIP SCAN)
Skip Scans are initiated by probing the index for distinct values of the prefix column. Each of these distinct values is then used as a starting point for a regular index search. The result is several separate searches of a single index that, when combined, eliminate the affect of the prefix column.
skip scan會探測出索引前導列的唯一值個數,每個唯一值都會作爲常規掃描的入口,在此基礎上做一次查找,最後合併 這些查詢。例如:表employees (sex, employee_id, address) ,有一個組合索引(sex, employee_id). 在索引跳躍的情況 下,我們可以邏輯上把他們看成兩個索引,一個是(男,employee_id),一個是(女,employee_id). select * from employees where employee_id=1;發出這個查詢後,oracle先進入sex爲男的入口,查找employee_id=1的條目。 再進入sex爲女的入口,查找employee_id=1的條目。最後合併兩個結果集
參考blog:http://www.itpub.net/thread-1372696-1-1.html
http://blog.csdn.net/dba_waterbin/article/details/8550405