Oracle 索引扫描的4种类型
根据索引的类型与where限制条件的不同,有4种类型的Oracle索引扫描: 3,4可归一种
(1) 索引唯一扫描(index uniquescan)
(2) 索引范围扫描(index range scan)
(3) 索引全扫描(index full scan)
(4) 索引快速扫描(index fast full scan)
(5) 索引跳跃扫描(INDEXSKIP SCAN)
一. 索引唯一扫描(index unique scan)
通过唯一索引查找一个数值经常返回单个ROWID,存在UNIQUE 或PRIMARY KEY 约束(它保证了语句只存取单行)的话,Oracle经常实现唯一性扫描
1 必须是通过唯一索引【UNIQUE】来访问数据;
2 通过唯一索引来访问数据时,每次返回的记录数必须是1条;
3 WHERE从句中必须要用等值【=】条件来过滤数据:
注意:表中某个字段是唯一的,如果该字段上的索引不是唯一的,那么CBO选择的将是通过INDEX RANGE SCAN的路径来访问数据。想要表达的是,真实场景下这种情况的确会存在,因为有些系统是通过应用程序来保证表中的字段唯一,而并没有在表上对应的字段上通过创建唯一约束或者是唯一索引来保证数据的唯一性。那么,这样的话,将会导致CBO在选择执行计划的情况下,有可能会出现偏差。所以,对于这种情况下,即使应用程序可以保证数据的唯一性,最好还是在表上创建一个唯一索引比较稳妥。
例子:
SQL> create table t as select * from dba_objects where object_id is not null;
表已创建。
SQL> alter table t modify (object_id not null);
表已更改。
SQL> create unique index index_t on t(object_id);
索引已创建。
SQL> select * from t a where a.object_id=75780;
执行计划
----------------------------------------------------------
Plan hash value: 4119349871
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Tim
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 207 | 2 (0)| 00:
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 207 | 2 (0)| 00:
|* 2 | INDEX UNIQUE SCAN | INDEX_T | 1 | | 1 (0)| 00:
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."OBJECT_ID"=75780)
统计信息
----------------------------------------------------------
6 recursive calls
0 db block gets
12 consistent gets
1 physical reads
0 redo size
1348 bytes sent via SQL*Net to client
404 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
二.索引范围扫描(index range scan)
使用一个索引存取多行数据,或者创建索引时没有提字为unique索引,即使返一行记录也走范围扫描.
使用index rang scan的3种情况:
(a) 在唯一索引列上使用了range操作符(> < <> >= <= between)。
(b) 在组合索引上(unique index),只使用部分列进行查询,导致查询出多行。
(c) 对非唯一索引列上进行的任何查询。
例子:
SQL> create table t as select * from dba_objects where object_id is not null;
表已创建。
SQL> alter table t modify (object_id not null);
表已更改。
SQL> create index index_t on t(object_id);
索引已创建。
SQL> set wrap off;
SQL> set autotrace traceonly;
SQL> select * from t a where a.object_id = 75780;
Plan hash value: 80339723
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Tim
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 207 | 2 (0)| 00:
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 207 | 2 (0)| 00:
|* 2 | INDEX RANGE SCAN | INDEX_T | 1 | | 1 (0)| 00:
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."OBJECT_ID"=75780)
Note
-----
- dynamic sampling used for this statement (level=2)
统计信息
----------------------------------------------------------
37 recursive calls
0 db block gets
118 consistent gets
1 physical reads
0 redo size
1444 bytes sent via SQL*Net to client
415 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
三. 索引全扫描(index full scan ) 索引快速扫描(index fast full scan)
索引全扫描(index full scan )
与全表扫描对应,也有相应的全Oracle索引扫描。在某些情况下,可能进行全Oracle索引扫描而不是范围扫描,需要注意的是全Oracle索引扫描只在CBO模式下才有效。 CBO根据统计数值得知进行全Oracle索引扫描比进行全表扫描更有效时,才进行全Oracle索引扫描,而且此时查询出的数据都必须从索引中可以直接得到。
例子:
SQL> create table t3 as select * from dba_objects where object_id is not null;
表已创建。
SQL> alter table t3 modify(object_id not null);
表已更改。
SQL> create index index_t3 on t3(object_id);
索引已创建。
SQL> set autotrace trraceonly;
SQL> select /*+index(t index_t3) */ object_id from t3 t;
已选择114164行。
执行计划
----------------------------------------------------------
Plan hash value: 3077935993
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 114K| 1451K| 270 (1)| 00:00:04 |
| 1 | INDEX FULL SCAN | INDEX_T3 | 114K| 1451K| 270 (1)| 00:00:04 |
-----------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
统计信息
----------------------------------------------------------
5 recursive calls
0 db block gets
7916 consistent gets
0 physical reads
0 redo size
1663703 bytes sent via SQL*Net to client
84126 bytes received via SQL*Net from client
7612 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
114164 rows processed
索引快速扫描(index fast full scan)
扫描索引中的所有的数据块,与 index full scan很类似,在这种存取方法中,可以使用多块读功能,也可以使用并行读入,以便获得最大吞吐量与缩短执行时间。
SQL> select object_id from t3;
已选择114164行。
执行计划
----------------------------------------------------------
Plan hash value: 448706225
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 114K| 1451K| 74 (0)| 00:00:01
| 1 | INDEX FAST FULL SCAN| INDEX_T3 | 114K| 1451K| 74 (0)| 00:00:01
--------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
统计信息
----------------------------------------------------------
19 recursive calls
0 db block gets
7931 consistent gets
253 physical reads
0 redo size
1663703 bytes sent via SQL*Net to client
84126 bytes received via SQL*Net from client
7612 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
114164 rows processed
index full scan的时候oracle定位到索引的root block,然后到branch block(如果有的话),再定位到第一个leaf block, 然后根据leaf block的双向链表顺序读取。它所读取的块都是有顺序的,也是经过排序的。
index fast full scan则不同,它是从段头开始,读取包含位图块,root block,所有的branch block, leaf block,读取的顺序完全有物理存储位置决定,并采取多块读,每次读取db_file_multiblock_read_count个块。
使用这两种索引扫描需要表的索引字段至少有一个是not null限制。快速全索引扫描比普通索引扫描速度快是因为快速索引扫描能够多块读取,并且能并行处理。
四. 索引跳跃扫描(INDEX SKIP SCAN)
索引跳跃扫描主要发生在对复合索引的列进行过滤时,没有写上先导列,并且先导列中的重复值较多,而非先导列中的重复数据较少。
SQL> create table t5 as select t.*,rownum id from user_tables t;
表已创建。
SQL> create index index_t5 on t5(table_name,id) ;
索引已创建。
SQL> begin
2 dbms_stats.gather_table_stats(ownname =>'BOC_RWA3' ,tabname => 'T5' );
3 end;
4 /
PL/SQL 过程已成功完成。
当在where 条件中出现前导列时为INDEX RANGE SCAN
select * from t5 t where table_name = 'T1';
执行计划
----------------------------------------------------------
Plan hash value: 1915796132
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 214 | 3 (0)| 00
| 1 | TABLE ACCESS BY INDEX ROWID| T5 | 1 | 214 | 3 (0)| 00
|* 2 | INDEX RANGE SCAN | INDEX_T5 | 1 | | 2 (0)| 00
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TABLE_NAME"='T1')
统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
4 consistent gets
0 physical reads
0 redo size
4144 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
当在where 条件中出现非前导列时为INDEX SKIP SCAN
SQL> select * from t5 t where id = 10;
执行计划
----------------------------------------------------------
Plan hash value: 2396816619
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 214 | 11 (0)| 00
| 1 | TABLE ACCESS BY INDEX ROWID| T5 | 1 | 214 | 11 (0)| 00
|* 2 | INDEX SKIP SCAN | INDEX_T5 | 1 | | 10 (0)| 00
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=10)
filter("ID"=10)
统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
13 consistent gets
0 physical reads
0 redo size
4153 bytes sent via SQL*Net to client
416 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
-----------------------------------------------------------
禁用skip scan:
alter system set “_optimizer_skip_scan_enabled” = false scope=spfile;