版权声明:本文发布于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
因为篇幅有限,关于索引扫描类型的总结到此为止。若存在内容上的遗漏,或者错误,欢迎各位指教。