一文讲透如何看懂Oracle索引执行类型(index unique scan,index range scan,index full scan,index fast full scan,index skip scan)

直入主题,因为看不懂Oracle的执行计划/解释计划(explain plan),我摸着石头过河,终于整理出一点关于oracle各种索引执行类型的小心得,希望能帮到面向百度编程各位!

 

index unique scan -- 索引唯一扫描

条件:

1. 列的值唯一

2. 用 '=' 来查询

 

index range scan -- 索引局部扫描

条件:

1. 列的值唯一

2. 范围查询(>, <, and...)

or

1. 列的值不唯一

2. 所有查询(=, >, <, and...)

 

index full scan -- 索引全局扫描

条件:

1. 必须是组合索引

2. 引导列不在where条件中

3. 必须是覆盖索引

PS: 当进行index full scan的时候 oracle定位到索引的root block,然后到branch block(如果有的话),再定位到第一个leaf block, 然后根据leaf block的双向链表顺序读取。它所读取的块都是有顺序的,也是经过排序的。

 

index fast full scan -- 索引快速全局扫描,不带order by 情况下常发生

如果select 语句后面中的列都被包含在组合索引中,而且where后面没有出现组合索引的引导列,并且需要检索出大部分数据,那么这个时候可能执行index fast full scan

条件:

1. 必须是组合索引

2. 引导列不在where条件中

3. 必须是覆盖索引

PS: index fast full scan则不同,它是从段头开始,读取包含位图块,root block, 所有的branch block, leaf block,读取的顺序完全由物理存储位置决定,并采取多块读,每次读取db_file_multiblock_read_count个。

 

所以:
1. 索引全扫描要排序,索引快速全扫描不用排序(索引全扫描会按照叶子块排序返回,而索引快速全扫描则是按照索引段内存储块顺序返回)。
2. 索引全扫描不得读取索引段头,而索引快速全扫描要读取索引段头

 

index skip scan -- 索引跳跃扫描,where 条件列是非索引的前导列情况下常发生

当查询可以通过组合索引得到结果,而且返回结果很少,并且where条件中没有包含索引引导列的时候,可能执行index skip scan

条件:

1. 必须是组合索引

2. 引导列不在where条件中

 

by index Rowid 列名 --Rowid 扫描是最快的访问数据方式

用主键查询或者回表查询时会出现

 

 

下列是我遇到的问题:

 

原SQL:

select accountid, validRecord from bi_TINbatchimportrecord 
where batchid = 'batchId1'
group by accountid, validrecord ;

原索引:

INDEX1 (BATCHID, ACCOUNTID, IMPORTTYPE, BUSINESSKEY)


INDEX2 (ACCOUNTID, BATCHID, VALIDRECORD, VALIDBATCHID)

 

问题 : 以为会用INDEX1,但用的是INDEX2

 

=================== 目前 INDEX2 ===================


用到INDEX2作为索引,以INDEX FAST FULL SCAN/INDEX SKIP SCAN的方式(where没有引导列,且索引是覆盖索引,根据索引具体COST来决定用哪种方式)

-- INDEX FAST FULL SCAN:
INDEX2索引树全树无序扫描,采取多块读的方式

-- INDEX SKIP SCAN:

select accountid, validRecord from bi_TINbatchimportrecord
where batchid = 'batchid'
----- Oracle优化 ----->
select accountid, validRecord from bi_TINbatchimportrecord
where accountid = 'acctid1' and batchid = 'batchid'
union
select accountid, validRecord from bi_TINbatchimportrecord
where accountid = 'acctid2' and batchid = 'batchid'
union
......

所以,accountid越集中,union次数越少,效率越高

 

=================== 强制 INDEX1 ===================

** 根据我当时浅薄的认知,复合索引应该依照最左原则,哪个复合索引的引导项是batchid,就应该用哪个


若强制使用INDEX1,用到BATCHID作为索引,先以RANGE SCAN的方式查到(ID, BATCHID, ACCOUNTID, IMPORTTYPE, BUSINESSKEY),找不到需要的validRecord,
再进行回表查询,效率取决于回表查的数据量

所以,索引不是覆盖索引的前提下,通过查出来 BATCHID='xxx' 的记录数越多,需要回表查询的记录越多,数据库是按大小分成数据片的,每次IO取一片,意味着回表记录越多,IO消耗越大

 

解决方案:

 

新增索引:

INDEX3 ( BATCHID, ACCOUNTID, VALIDRECORD)

=================== DB引擎自动选择 INDEX3 作为索引 ===================

因为:

1. 其是覆盖索引,复合索引的列涵盖了所有搜索列和条件列

2. 符合复合索引的最左原则

 

所以:

以INDEX RANGE SCAN执行

 

=================== 两组实验数据 ===================

 

----- 第一组 -----
BATCHID1 = 'BatchId1' -> 326

INDEX1 COST = 10(索引查询IO) + 305(回表IO) + 1(无序Group by的CPU占用) = 316
INDEX2 COST = 4319(无引导列且覆盖索引查询IO) + 1(无序Group by的CPU占用) = 4320
INDEX3 COST = 8(有引导列且覆盖索引查询IO) + 0(有序Group by的CPU占用) = 8
no index COST = 67259(全表扫描IO) + 1(无序Group by的CPU占用) = 67260

----- 第二组 -----
BATCHID2 = 'BatchId2' -> 231566

INDEX1 COST = 3510(索引查询IO) + 213667(回表IO) + 1418(无序Group by的CPU占用) = 218595
INDEX2 COST = 4319(无引导列且覆盖索引查询IO) + 1342(无序Group by的CPU占用) = 5661
INDEX3 COST = 2870(有引导列且覆盖索引查询IO) + 0(有序Group by的CPU占用) = 2870
no index COST = 67260(全表扫描IO) + 1341(无序Group by的CPU占用) = 68610

 


=================== COST ===================
INDEX1 cost:索引查询IO + 回表IO + 条件查询CPU消耗

INDEX2 cost: 无引导列且覆盖索引查询IO + 条件查询CPU消耗

INDEX3 cost: 有引导列且覆盖索引查询IO + 条件查询CPU消耗

no index cost:全表扫描IO + 条件查询CPU消耗

 

posted @ 2021-01-03 21:25  苏天后  阅读(1869)  评论(0编辑  收藏  举报