index full scan , 扫描root block ---> branch block ----> leaf block ,扫描leaf block的时候是有序的,可以从左往右,也可以从右往左,返回的结果也是有序的,并且是但块读
index fast full scan 扫描 索引段头(单块读) --->root block---->branch block----->leaf block
下面来做个实验(基于Oracle10g,段自动管理)
create table test as select * from dba_objects;
alter table test modify owner not null;
create index idx_owner on test(owner);
select /*+ index(test idx_owner) */ owner from test where rownum<1001; ----它会走 index full scan
select /*+ index_ffs(test idx_owner) */ owner from test where rownum<1001; ----它会走 index fast full scan
对 index full scan 的SQL 进行10046跟踪,跟踪的时候记得设置下面参数
set arraysize 1000;
alter system flush buffer_cache; ----一定要刷新,不然观察不到 db file sequential read
alter system flush shared_pool;
alter session set events '10046 trace name context forever, level 8';
select /*+ index(test idx_owner) */ owner from test where rownum<1001;
alter session set events '10046 trace name context off';
找到10046 trace 文件
===================== PARSING IN CURSOR #1 len=69 dep=0 uid=54 oct=3 lid=54 tim=271045223908 hv=4111226343 ad='1c118ecc' select /*+ index(test idx_owner) */ owner from test where rownum<1001 END OF STMT PARSE #1:c=78001,e=192236,p=26,cr=165,cu=0,mis=1,r=0,dep=0,og=1,tim=271045223905 EXEC #1:c=0,e=14,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=271045224086 WAIT #1: nam='SQL*Net message to client' ela= 2 driver id=1111838976 #bytes=1 p3=0 obj#=10234 tim=271045224133 WAIT #1: nam='db file sequential read' ela= 8887 file#=4 block#=3468 blocks=1 obj#=53777 tim=271045233777 WAIT #1: nam='db file sequential read' ela= 277 file#=4 block#=3469 blocks=1 obj#=53777 tim=271045234190 FETCH #1:c=0,e=10124,p=2,cr=2,cu=0,mis=0,r=1,dep=0,og=1,tim=271045234294 WAIT #1: nam='SQL*Net message from client' ela= 324 driver id=1111838976 #bytes=1 p3=0 obj#=53777 tim=271045234689 WAIT #1: nam='SQL*Net message to client' ela= 1 driver id=1111838976 #bytes=1 p3=0 obj#=53777 tim=271045234789 WAIT #1: nam='SQL*Net more data to client' ela= 9 driver id=1111838976 #bytes=2002 p3=0 obj#=53777 tim=271045235017 WAIT #1: nam='db file sequential read' ela= 286 file#=4 block#=3470 blocks=1 obj#=53777 tim=271045235371 WAIT #1: nam='SQL*Net more data to client' ela= 9 driver id=1111838976 #bytes=2000 p3=0 obj#=53777 tim=271045235550 WAIT #1: nam='db file sequential read' ela= 321 file#=4 block#=3471 blocks=1 obj#=53777 tim=271045235939 FETCH #1:c=0,e=1279,p=2,cr=3,cu=0,mis=0,r=999,dep=0,og=1,tim=271045236055 WAIT #1: nam='SQL*Net message from client' ela= 8914715 driver id=1111838976 #bytes=1 p3=0 obj#=53777 tim=271054150848 STAT #1 id=1 cnt=1000 pid=0 pos=1 obj=0 op='COUNT STOPKEY (cr=5 pr=4 pw=0 time=10119 us)' STAT #1 id=2 cnt=1000 pid=1 pos=1 obj=53777 op='INDEX FULL SCAN IDX_OWNER (cr=5 pr=4 pw=0 time=10116 us)' =====================
扫描的第一个block是3468,然后3469,3470,3471
SQL> select header_file,header_block from dba_segments where segment_name='IDX_OWNER';
HEADER_FILE HEADER_BLOCK
----------- ------------
4 3467
SQL> select name,height from index_stats where name='IDX_OWNER';
NAME HEIGHT
------------------------------ ----------
IDX_OWNER 2
这个索引的段头块是3467,root block就是段头+1 ,这里 root block 就是3468 ,根据实验可知,index full scan 没有扫描 segment header ,而是直接扫描 root block
再来看 index fast full scan 实验
set arraysize 1000;
alter system flush buffer_cache;
alter system flush shared_pool;
alter session set events '10046 trace name context forever, level 8';
select /*+ index_ffs(test idx_owner) */ owner from test where rownum<1001;
alter session set events '10046 trace name context off';
找到10046 trace文件
===================== PARSING IN CURSOR #4 len=73 dep=0 uid=54 oct=3 lid=54 tim=272536163194 hv=1795841612 ad='18686370' select /*+ index_ffs(test idx_owner) */ owner from test where rownum<1001 END OF STMT PARSE #4:c=46801,e=206181,p=14,cr=55,cu=0,mis=1,r=0,dep=0,og=1,tim=272536163192 EXEC #4:c=0,e=13,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=272536163332 WAIT #4: nam='SQL*Net message to client' ela= 2 driver id=1111838976 #bytes=1 p3=0 obj#=10234 tim=272536163375 WAIT #4: nam='db file sequential read' ela= 8670 file#=4 block#=3467 blocks=1 obj#=53777 tim=272536172783 WAIT #4: nam='db file scattered read' ela= 571 file#=4 block#=3468 blocks=5 obj#=53777 tim=272536173489 FETCH #4:c=15600,e=10219,p=6,cr=7,cu=0,mis=0,r=1,dep=0,og=1,tim=272536173634 WAIT #4: nam='SQL*Net message from client' ela= 255 driver id=1111838976 #bytes=1 p3=0 obj#=53777 tim=272536173968 WAIT #4: nam='SQL*Net message to client' ela= 1 driver id=1111838976 #bytes=1 p3=0 obj#=53777 tim=272536174046 WAIT #4: nam='SQL*Net more data to client' ela= 8 driver id=1111838976 #bytes=2002 p3=0 obj#=53777 tim=272536174209 WAIT #4: nam='SQL*Net more data to client' ela= 6 driver id=1111838976 #bytes=2000 p3=0 obj#=53777 tim=272536174344 FETCH #4:c=0,e=398,p=0,cr=3,cu=0,mis=0,r=999,dep=0,og=1,tim=272536174432 *** 2012-03-02 14:25:15.649 WAIT #4: nam='SQL*Net message from client' ela= 13715652 driver id=1111838976 #bytes=1 p3=0 obj#=53777 tim=272549890151 STAT #4 id=1 cnt=1000 pid=0 pos=1 obj=0 op='COUNT STOPKEY (cr=10 pr=6 pw=0 time=10214 us)' STAT #4 id=2 cnt=1000 pid=1 pos=1 obj=53777 op='INDEX FAST FULL SCAN IDX_OWNER (cr=10 pr=6 pw=0 time=10209 us)' =====================
最开始扫描的是3467,它是索引的段头,并且是单块读(注意:段头都是单块读),然后才是从3468 开始扫描,一共扫描了5个block 3468就是索引的root block
这里也知道了,无论哪种索引扫描,都会扫描 root block
为什么 index fast full scan 要扫描 segment header呢?因为 index fast full scan 需要扫描所有的索引块(leaf block),并且扫描不是有序的,是多块读,而且它不会回表,也就是说它不会解析出rowid,正是由于它要扫描所有的leaf block,并且是离散读,所以它必须读取segment header,不然oracle怎么知道它读取了所有的 leaf block?
为什么 index full scan 不扫描segment header? 因为 index full scan 是连续读的,由于leaf block之间有双向指针,Oracle不需要扫描segment header就能判断 leaf block 扫描完了没,它只需要从左往右,或者从右往左一直扫描到尽头即可。