版權聲明:本文發佈於http://www.cnblogs.com/yumiko/,版權由Yumiko_sunny全部,歡迎轉載。轉載時,請在文章明顯位置註明原文連接。若在未經做者贊成的狀況下,將本文內容用於商業用途,將保留追究其法律責任的權利。若是有問題,請以郵箱方式聯繫做者(793113046@qq.com)。sql
理解oracle索引掃描類型的特色以及具體觸發的條件,對於經過合理地使用索引,進行sql優化相當重要(例如組合索引的引導列的選擇問題)。oracle
在總結索引掃描類型前,須要再次強調關於索引特色的幾個關鍵點:ide
其餘的一些特色,可參閱前面的幾篇總結。測試
此外,爲避免概念的混淆,再次說明一下:「索引類型」主要探討的是索引的幾種類別的問題,而「索引掃描類型」主要探討的是索引掃描的幾種具體實現方法的問題。字體
Oracle提供了五種索引掃描類型,根據具體索引類型、數據分佈、約束條件以及where限制的不一樣進行選擇: 優化
索引惟一掃描,僅僅針對惟一索引的掃描,且僅適用於等值(=)條件的查詢。從結果集看,至多返回一條記錄。spa
具體狀況分析:code
須要注意:orm
示例:htm
--建立惟一索引
Yumiko@Sunny >create unique index ind_test_normal on test_normal(empno,ENAME,SAL); Index created.
Yumiko@Sunny >select INDEX_NAME,INDEX_TYPE,TABLE_NAME,UNIQUENESS from user_indexes where TABLE_NAME='TEST_NORMAL'; INDEX_NAME INDEX_TYPE TABLE_NAME UNIQUENES ------------------------- --------------------------- --------------- --------- IND_TEST_NORMAL NORMAL TEST_NORMAL UNIQUE
--查詢記錄並查看執行計劃 Yumiko@Sunny >select * from TEST_NORMAL where empno=7369 and ENAME='SMITH' and sal=800; EMPNO ENAME JOB SAL ---------- ---------- --------- ---------- 7369 SMITH CLERK 800 Execution Plan ---------------------------------------------------------- Plan hash value: 1399315988 ------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes|Cost (%CPU)| Time | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1| 39| 1 (0)| 00:00:01| | 1 | TABLE ACCESS BY INDEX ROWID| TEST_NORMAL | 1| 39| 1 (0)| 00:00:01| |* 2 | INDEX UNIQUE SCAN | IND_TEST_NORMAL| 1| | 0 (0)| 00:00:01| ------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPNO"=7369 AND "ENAME"='SMITH' AND "SAL"=800) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 2 consistent gets 16 physical reads 0 redo size 581 bytes sent via SQL*Net to client 458 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
索引範圍掃描,不只能夠針對惟一索引,也能夠針對非惟一索引。從結果集看,能夠是一條記錄,也能夠是多條記錄。
具體狀況分析:
須要注意:
示例:
--建立普通索引
Yumiko@Sunny >create index ind_test_normal on test_normal(empno,ENAME,SAL); Index created.
--驗證普通索引建立狀況,確認爲非惟一索引 Yumiko@Sunny >select INDEX_NAME,INDEX_TYPE,TABLE_NAME,UNIQUENESS from user_indexes where TABLE_NAME='TEST_NORMAL'; INDEX_NAME INDEX_TYPE TABLE_NAME UNIQUENES ------------------------- --------------------------- --------------- --------- IND_TEST_NORMAL NORMAL TEST_NORMAL NONUNIQUE
--查詢記錄並觀察執行計劃,此時掃描類型爲index range scan Yumiko@Sunny >select * from TEST_NORMAL where empno=7369 and ENAME='SMITH' and sal=800; EMPNO ENAME JOB SAL ---------- ---------- --------- ---------- 7369 SMITH CLERK 800 Execution Plan ----------------------------------------------- Plan hash value: 67814702 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 18 | 2 (0)| 00:00:01| | 1 | TABLE ACCESS BY INDEX ROWID| TEST_NORMAL | 1 | 18 | 2 (0)| 00:00:01| |* 2 | INDEX RANGE SCAN | IND_TEST_NORMAL| 1 | | 1 (0)| 00:00:01| --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPNO"=7369 AND "ENAME"='SMITH' AND "SAL"=800) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 3 consistent gets 16 physical reads 0 redo size 717 bytes sent via SQL*Net to client 469 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
該索引掃描方式主要發生在組合索引上,且組合索引的引導列未被指定在檢索條件中的狀況下發生。
在組合索引中,不管該索引是否爲惟一索引。當引導列未被指定在檢索條件的狀況下,可能會發生「索引跳躍掃描」
如下黃色字體內容摘引自http://book.51cto.com/art/201312/422441.htm
對於組合索引,在無前導列的狀況下還能使用索引,是由於Oracle幫咱們對該索引的前導列的全部distinct值作了遍歷。
所謂的對目標索引的全部distinct值作遍歷,其實際含義至關於對原目標SQL作等價改寫(即把要用的目標索引的全部前導列的distinct值都加進來)。
例如:create index idx_employee on employee(gender,employee_id),表數據量10000行,其中gender列只有「M」跟「F」值。
當執行select * from employee where employee_id = 100時,至關於執行了等價改寫,改寫爲:
select * from employee where gender = 'F' and employee_id = 100
union all
select * from employee where gender = 'M' and employee_id = 100;
所以,Oracle中的索引跳躍式掃描僅僅適用於那些目標索引前導列的distinct值數量較少、後續非前導列的可選擇性又很是好的情形,由於索引跳躍式掃描的執行效率必定會隨着目標索引前導列的distinct值數量的遞增而遞減。不然將執行全表掃描。
示例:
--查詢測試表的總數據量
--該表總數據爲22928行
Yumiko@Sunny >select count(*) from test; COUNT(*) ---------- 22928
--查詢測試表中owner列的列值數據分佈狀況
--從查詢結果看,對於owner列,在22928行的數據中,只分布了3個不一樣的列值,數據數值的分佈狀況較爲集中 Yumiko@Sunny >select count(distinct owner) from test; COUNT(DISTINCTOWNER) -------------------- 3
--查詢測試表中object_id列的數值分佈狀況
--從查詢結果看,對於object_id列,在22928行的數據中,分佈了22912個不一樣的數值,該列總體的數值分佈狀況較爲零散 Yumiko@Sunny >select count(distinct object_id) from test; COUNT(DISTINCTOBJECT_ID) ------------------------ 22912 Yumiko@Sunny >desc test Name Null? Type ---------------------------------------------------------------------- OWNER VARCHAR2(30) OBJECT_NAME VARCHAR2(128) SUBOBJECT_NAME VARCHAR2(30) OBJECT_ID NUMBER DATA_OBJECT_ID NUMBER OBJECT_TYPE VARCHAR2(19) CREATED DATE LAST_DDL_TIME DATE TIMESTAMP VARCHAR2(19) STATUS VARCHAR2(7) TEMPORARY VARCHAR2(1) GENERATED VARCHAR2(1) SECONDARY VARCHAR2(1)
--以數值較爲集中的owner做爲組合索引的引導列,建立普通索引 Yumiko@Sunny >create index test on test(owner,OBJECT_ID); Index created. Yumiko@Sunny >select INDEX_NAME,INDEX_TYPE,TABLE_NAME,UNIQUENESS from user_indexes where TABLE_NAME='TEST'; INDEX_NAME INDEX_TYPE TABLE_NAME UNIQUENES ------------------------- ------------------- --------------- --------- TEST NORMAL TEST NONUNIQUE --收集測試表最新的統計信息 Yumiko@Sunny >analyze table test compute statistics for table for all columns for all indexes; Table analyzed.
--清空buffer cache緩衝池,保證無測試表的數據塊存在在內存中,防止影響到測試結果 Yumiko@Sunny >alter system flush buffer_cache; System altered.
--使用以前建立的組合索引,以組合索引非引導做爲條件查詢列進行條件查詢
--從執行計劃看,oracle用到了索引掃描,並且採用index skip scan的方式進行掃描 Yumiko@Sunny >select * from test where object_id=3; Execution Plan ---------------------------------------------------------- Plan hash value: 2389257771 ---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes| Cost (%CPU)| Time | ---------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 86| 5 (0)| 00:00:01| | 1 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 86| 5 (0)| 00:00:01| |* 2 | INDEX SKIP SCAN | TEST | 1 | | 4 (0)| 00:00:01| ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_ID"=3) filter("OBJECT_ID"=3) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 6 consistent gets 24 physical reads 0 redo size 1402 bytes sent via SQL*Net to client 469 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
--刪除以前的索引 Yumiko@Sunny >drop index test; Index dropped.
--以數值分佈較爲零散的object_id列做爲引導列,再次建立普通索引 Yumiko@Sunny >create index test on test(OBJECT_ID,owner); Index created.
--收集測試表最新的統計信息 Yumiko@Sunny >analyze table test compute statistics for table for all columns for all indexes; Table analyzed.
--清空buffer cache緩衝池,避免影響數據測試 Yumiko@Sunny >alter system flush buffer_cache; System altered.
--使用上面剛剛建立的組合索引的非引導列owner做爲條件查詢列進行查詢操做
--從執行計劃看,這次oracle未選擇走索引掃描,而是採用了全表掃描的方式
--到此,印證了以前對於index skip scan方式選擇的說法 Yumiko@Sunny >select * from test where owner='BI'; 8 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 1357081020 ------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes| Cost (%CPU)| Time | ------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 8 | 688| 74 (2)| 00:00:01| |* 1 | TABLE ACCESS FULL| TEST | 8 | 688| 74 (2)| 00:00:01| ------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OWNER"='BI') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 318 consistent gets 315 physical reads 0 redo size 1583 bytes sent via SQL*Net to client 469 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 8 rows processed
對於索引全掃描,就是使用目標索引進行索引掃描時,會掃描全部索引葉塊的全部索引行。
對於索引全掃描,只適用於CBO。
對於索引全掃描,使用單塊讀取的方式,有序讀取索引塊。
對於索引全掃描,從結果集看,結果所有源於索引塊,並且因爲已經按照索引鍵值順序排序,所以不須要單獨排序
對於索引全掃描,會話會產生db file sequential reads事件。
具體狀況分析:
示例:
--爲測試表TEST_NORMAL的empno列添加非空約束
Yumiko@Sunny >alter table TEST_NORMAL modify(empno not null); Table altered.
--以empno列以及ename列做爲組合,建立普通索引 Yumiko@Sunny >create index TEST_NORMAL_ind on TEST_NORMAL(empno,ename); Index created.
--查詢上面組合索引涉及的ename列,該列不存在非空約束
--從執行計劃看,oracle選擇了index full scan的索引掃描方式,且結果集僅僅來源於索引塊(沒有根據rowid返回數據集的記錄,說明無訪問數據塊) Yumiko@Sunny >select ename from TEST_NORMAL; 14 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 2425626010 ------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 14 | 70 | 1 (0)| 00:00:01 | | 1 | INDEX FULL SCAN | TEST_NORMAL_IND | 14 | 70 | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------ Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 2 consistent gets 8 physical reads 0 redo size 705 bytes sent via SQL*Net to client 469 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 14 rows processed
對於索引快速掃描,就是使用目標索引進行索引掃描時,會掃描全部索引葉塊的全部索引行。
對於索引快速掃描,只適用於CBO。
對於索引快速掃描,使用多塊讀取的方式,讀取索引塊。(這種方式相較於索引全掃描,獲取數據的效率更高)
對於索引快速掃描,從結果集看,結果所有源於索引塊,但數據結果不必定有序。
對於索引快速掃描,會話會產生db file scattered reads事件。
具體狀況分析:
參閱上面的索引全掃描,二者所謂的同宗,只是不一樣的需求(對於結果的響應速度或是數據序列,固然CBO優化器也會進行內部的計算評估選取最優執行路徑),產生的不一樣的執行結果,下面的示例會展現
示例:
--查詢測試表的數據量,顯示此表有22928行數據
Yumiko@Sunny >select count(*) from test; COUNT(*) ---------- 22928
--確認數據列中不含非空約束 Yumiko@Sunny >desc test Name Null? Type -------------------------------------------------------------------------------- OWNER VARCHAR2(30) OBJECT_NAME VARCHAR2(128) SUBOBJECT_NAME VARCHAR2(30) OBJECT_ID NUMBER DATA_OBJECT_ID NUMBER OBJECT_TYPE VARCHAR2(19) CREATED DATE LAST_DDL_TIME DATE TIMESTAMP VARCHAR2(19) STATUS VARCHAR2(7) TEMPORARY VARCHAR2(1) GENERATED VARCHAR2(1) SECONDARY VARCHAR2(1)
--針對object_id列,建立普通索引 Yumiko@Sunny >create index test on test(object_id); Index created.
--確認上面建立的索引爲非惟一索引 Yumiko@Sunny >select INDEX_NAME,INDEX_TYPE,TABLE_NAME,UNIQUENESS from user_indexes where TABLE_NAME='TEST'; INDEX_NAME INDEX_TYPE TABLE_NAME UNIQUENES ------------------------------------------------------------------------- TEST NORMAL TEST NONUNIQUE
--清空buffer_cache緩衝池數據,防止形成測試的影響 Yumiko@Sunny >alter system flush buffer_cache; System altered.
--清空shared pool緩衝池數據,防止對測試形成影響 Yumiko@Sunny >alter system flush shared_pool; System altered.
--收集測試表最新的統計信息 Yumiko@Sunny >analyze table test compute statistics for table for all columns for all indexes; Table analyzed.
--打開會話追蹤,僅查看執行計劃 Yumiko@Sunny >set autotrace trace --查詢索引列object,同時使用is not null做爲查詢條件。
--因爲b-tree索引中不記錄null值信息,並且該列不存在非空約束,經過not null條件指定,讓oracle沒必要考慮null值。
--從執行計劃看,因爲讀取的數據量很大且不用排序,oracle選擇了更快的多塊讀方式的index fast full scan,儘快獲取數據。
--一樣的,此時未出現access by index rowid的狀況,說明未查詢數據塊,僅僅查詢了索引塊。 Yumiko@Sunny >select object_id from test where object_id is not null; 22928 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 1645531115 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 23290 | 295K| 14 (0)| 00:00:01 | |* 1 | INDEX FAST FULL SCAN| TEST | 23290 | 295K| 14 (0)| 00:00:01 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_ID" IS NOT NULL) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 1582 consistent gets 53 physical reads 0 redo size 502642 bytes sent via SQL*Net to client 17277 bytes received via SQL*Net from client 1530 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 22928 rows processed --修改上面的查詢,增長排序操做。
--從執行計劃看,因爲這次增長了排序操做,oracle選擇了偏向有序讀取的index full scan的方式進行掃描。
--一樣的,這次未查詢數據塊(不存在access by index rowid)。 Yumiko@Sunny >select object_id from test where object_id is not null order by object_id; 22928 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 3883652822 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 23290 | 295K| 60 (2)| 00:00:01 | |* 1 | INDEX FULL SCAN | TEST | 23290 | 295K| 60 (2)| 00:00:01 | ------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_ID" IS NOT NULL) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 1578 consistent gets 64 physical reads 0 redo size 502642 bytes sent via SQL*Net to client 17277 bytes received via SQL*Net from client 1530 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 22928 rows processed --再次修改查詢,取消not null條件查詢以及排序操做。
--此時,對於沒有非空約束的object_id列,增長了null值的可能性,而b-tree索引不保存null信息。
--這次,oracle選擇了全表掃描的方式。 Yumiko@Sunny >select object_id from test; 22928 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 1357081020 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 22928 | 91712 | 74 (2)| 00:00:01 | | 1 | TABLE ACCESS FULL| TEST | 22928 | 91712 | 74 (2)| 00:00:01 | -------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 1830 consistent gets 315 physical reads 0 redo size 415626 bytes sent via SQL*Net to client 17277 bytes received via SQL*Net from client 1530 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 22928 rows processed
因爲篇幅有限,關於索引掃描類型的總結到此爲止。若存在內容上的遺漏,或者錯誤,歡迎各位指教。