查看原文
其他

为什么INDEX FAST FULL SCAN需要排序?

2015-04-29 盖国强 云和恩墨

创建索引后由于需要查询的字段都可以通过索引获取,所以不再需要访问具体的数据表,数据库通过INDEXFAST FULL SCAN来完成查询,由于查询包含一个ORDER BY排序运算,执行计划中显示了SORTORDER BY的过程,统计数据中显示了查询产生了一次内存排序:


SQL> select eeode,cname,shortcode from cnderp.eeode whereeeode>' ' order by eeode

2 /

------------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes |TempSpc |Cost (%CPU) |
------------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 66567 | 2470K | | 761 (2) |
| 1 | SORTORDER BY | | 66567 | 2470K | 6280K | 761 (2) |
| 2 | INDEX FAST FULL SCAN | EEODE_EEODE_CNAME | 66567 | 2470K | | 100 (1) |
------------------------------------------------------------------------------------------

446 consistent gets
1 sorts (memory)
0 sorts (disk)
66566 rows processed


大家都知道索引是有序的数据存储,那么为什么INDEX FAST FULL SCAN需要排序呢?INDEXFAST FULL SCAN与常见的INDEX FULL SCAN又有何区别呢?


图13-1是最常用的B*Tree索引示意图:



图13-1 最常用的B*Tree索引示意图


基于索引的有序存储,如果读取时从叶节点自左向右读取,读取出来的数据就将是有序的,如果读取扫描一定的范围就是通常所说的INDEXRANGE SCAN;如果顺序读取所有的叶节点这就是INDEX FULL SCAN。


由于顺序读取的数据是有序的,INDEX FULL SCAN可以消除某些查询的排序操作;而INDEXFAST FULL SCAN是指通过多块读读取全部索引的数据块,数据读取时,是按照块的连续性,以Extent为单位来读取的(如同进行全表扫描一样),所以获取的数据是无序的。


这就是几种常见索引访问的区别。


通过测试来具体看一下几种索引访问方式的异同,以下测试来自Oracle10gR2版本。


SQL> select * from v$version;

BANNER

----------------------------------------------------------------

Oracle Database 10g Enterprise EditionRelease 10.2.0.3.0 - Prod

PL/SQL Release 10.2.0.3.0 - Production

CORE 10.2.0.3.0 Production

TNS for Linux: Version 10.2.0.3.0 -Production

NLSRTL Version 10.2.0.3.0 – Production


首先基于DBA_OBJECTS创建一个测试表,并创建索引:


SQL> create table eygle as select *from dba_objects where 1=0;

Table created.

SQL> create index idx_object_id oneygle(object_id);

Index created.

SQL> insert into eygle select *from dba_objects where rownum <10001;


10000 rows created.

SQL> commit;

Commit complete.

SQL> alter table eygle modify(object_id not null);

Table altered.


分析数据,收集统计信息:


SQL> analyze table eygle compute statistics for table for all indexes forall indexed columns;

Table analyzed


做一个10条记录输出的间查询,默认的数据库选择了INDEX FASTFULL SCAN,使用了7个逻辑读:


SQL> set autotrace on

SQL> select object_id from eyglewhere rownum <11;

OBJECT_ID

----------

1119
1120
1121
1122
1123
1124
1125
1126
1127
1128


10 rows selected.

Execution Plan

----------------------------------------------------------

Plan hash value: 372913538


----------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes | Cost (%CPU) | Time |
----------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 10 | 40 | 2 (0) | 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | INDEX FAST FULL SCAN | IDX_OBJECT_ID | 10 | 40 | 2 (0) | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified byoperation id):

---------------------------------------------------

1 - filter(ROWNUM<11)

Statistics

----------------------------------------------------------

0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
10 rows processed


如果手工指定索引,此时数据库选择了INDEX FULL SCAN,使用了3个逻辑读,输出的结果自低至高,是从索引的最左端开始读取的:


SQL> select /*+ index(eygleidx_object_id) */ object_id from eygle where rownum <11;

OBJECT_ID

----------
2
3
4
5
6
7
8
9
10
11

10 rows selected.


Execution Plan

----------------------------------------------------------

Plan hash value: 3614676306

-----------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes | Cost (%CPU) |Time |
-----------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 10 | 40 | 2 (0) | 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | INDEXFULL SCAN | IDX_OBJECT_ID | 10 | 40 | 2 (0) | 00:00:01 |
-----------------------------------------------------------------------------------

Predicate Information (identified byoperation id):

---------------------------------------------------

1 - filter(ROWNUM<11)

Statistics

----------------------------------------------------------

1 recursive calls
0 db block gets
3 consistentgets
0 physical reads
10 rows processed


如果增加ORDER BY子句,INDEX FAST FULL SCAN则须要排序:


SQL> select/*+ index_ffs(eygle idx_object_id) */ object_id

2from eygle where rownum <11 order by object_id;

10 rows selected.


Execution Plan

----------------------------------------------------------

Plan hash value: 1286843348


---------------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes |TempSpc |Cost (%CPU) |
---------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 10 | 40 | | 40 (8) |
| 1 | SORTORDER BY | | 10 | 40 | 248K | 40 (8) |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | INDEX FAST FULL SCAN | IDX_OBJECT_ID | 10000 | 40000 | | 6 (0) |
---------------------------------------------------------------------------------------


而INDEX FULL SCAN 则无须排序:


SQL> select object_id from eyglewhere rownum <11 order by object_id;

10 rows selected.

Execution Plan

----------------------------------------------------------

Plan hash value: 3614676306


-----------------------------------------------------------------------------------
| Id | Operation |Name | Rows | Bytes | Cost (%CPU) | Time |
-----------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 10 | 40 | 2 (0) | 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | INDEXFULL SCAN | IDX_OBJECT_ID | 10000 |40000 | 2 (0) | 00:00:01 |
-----------------------------------------------------------------------------------


文章来源:《Oracle DBA手记1》第13章 索引访问与数据读取 作者:盖国强

配图来源:http://cn.dreamstime.com/

回复“手记1”可下载本书前两篇内容:DBA工作手记/诊断案例篇(建议复制链接至电脑端)


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存