h2 { margin-top: 0.46cm; margin-bottom: 0.46cm; direction: ltr; line-height: 173%; text-align: justify; page-break-inside: avoid }
h2.western { font-family: "Cambria", serif; font-size: 16pt }
h2.cjk { font-family: "宋体"; font-size: 16pt }
h2.ctl { font-size: 16pt }
h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; line-height: 241%; text-align: justify; page-break-inside: avoid }
h1.western { font-family: "Calibri", serif; font-size: 22pt }
p { margin-bottom: 0.25cm; direction: ltr; line-height: 120%; text-align: justify }
a:link { color: rgb(0, 0, 255) }

1.创建测试表

SQL>
CREATE TABLE TEST AS SELECT * FROM dba_objects WHERE 0=1;

2.创建测试索引

SQL>
CREATE INDEX ind_test_id ON TEST(object_id);

3.插入测试数据

SQL>
INSERT INTO TEST SELECT * FROM dba_objects WHERE
object_id IS NOT NULL AND object_id > 10000 ORDER BY object_id
DESC;

17837
rows created.

4.分析表
附带索引等等

SQL>
analyze table test compute statistics for table for all columns for
all indexes;

Table
analyzed.

5.打开执行计划

SQL>
set autotrace trace;

6.FFS示例

SQL>
select object_id from test;

17837
rows selected.

Execution
Plan

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

0
SELECT STATEMENT Optimizer=CHOOSE (Cost=68 Card=17837
Bytes=71348)

1
0 TABLE ACCESS (FULL) OF 'TEST' (Cost=68 Card=17837 Bytes=71348)

这时候
Oracle会选择全表扫描,因为
object_id
列默认是可以为null的,来修改成
not null

6.1修改字段属性
not null

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

6.2再次验证
FFS

SQL>
select object_id from test;

17837
rows selected.

Execution
Plan

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

0
SELECT STATEMENT Optimizer=CHOOSE (Cost=11 Card=17837
Bytes=71348)

1
0 INDEX (FAST FULL SCAN) OF 'IND_TEST_ID' (NON-UNIQUE) (Cost=11
Card=17837 Bytes=71348)

没有问题

7. IFS
示例

SQL>
select/*+ index(test ind_TEST_ID)*/ object_id from test;

17837
rows selected.

Execution
Plan

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

0
SELECT STATEMENT Optimizer=CHOOSE (Cost=41 Card=17837
Bytes=71348)

1
0 INDEX (FULL SCAN) OF 'IND_TEST_ID' (NON-UNIQUE) (Cost=101
Card=17837 Bytes=71348)

没有问题

我们看到了两者都可以在这种情况下使用,那么他们有什么区别呢?有个地方可以看出两者的区别,
来看一下两者的输出结果,为了让大家看清楚一点,我们只取10行。

8结果验证

SQL>
set arraysize 1000;

SQL>
alter system flush buffer_cache; ----一定要刷新,不然观察不到
db file sequential
read

SQL>
alter system flush shared_pool;

SQL>
alter session set events '10046 trace name context forever, level 8';

8.1FFS(INDEX
FAST FULL SCAN)

SQL>
select object_id from test where rownum<11;

OBJECT_ID

----------

66266

66267

66268

66269

66270

66271

66272

66273

66274

66275

10
rows selected.

SQL>
alter session set events '10046 trace name context off';

检查该索引所属文件号、段头快

SQL>
select owner,header_file,header_block from dba_segments where
segment_name='IND_TEST_ID';

OWNER
HEADER_FILE HEADER_BLOCK

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

OWNER
4 3562

段头块为
3562,后退一个即
索引的 root block
3563

SQL>
set arraysize 1000;

SQL>
alter system flush buffer_cache; ----一定要刷新,不然观察不到
db file sequential
read

SQL>
alter system flush shared_pool;

SQL>
alter session set events '10046 trace name context forever, level 8';

以下内容取自
10046 event trace文件

=====================

PARSING
IN CURSOR #2 len=42 dep=0 uid=88 oct=3 lid=88 tim=1478672879417440
hv=3715463873 ad='cf77db60' sqlid='9rkncnzfrayq1'

select
object_id from test where rownum<11

END
OF STMT

PARSE
#2:c=12998,e=13339,p=15,cr=61,cu=0,mis=1,r=0,dep=0,og=1,plh=1931801113,tim=1478672879417411

EXEC
#2:c=0,e=103,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1931801113,tim=1478672879417635

WAIT
#2: nam='SQL*Net message to client' ela= 12 driver id=1650815232
#bytes=1 p3=0 obj#=0 tim=1478672879417740

WAIT
#2: nam='db file sequential read' ela= 24 file#=4 block#=3562
blocks=1 obj#=81680 tim=1478672879417839 --第四个数据文件的
3562数据块
也就是从段头块开始 ,依次读取 3563数据块
。3563数据块一次读入5个数据块

WAIT
#2: nam='db file sequential read' ela= 11 file#=4 block#=21761
blocks=1 obj#=81680 tim=1478672879417916

WAIT
#2: nam='db file sequential read' ela= 7 file#=4 block#=3561
blocks=1 obj#=81680 tim=1478672879417940

WAIT
#2: nam='db file scattered read' ela= 5 file#=4 block#=11008
blocks=2 obj#=81680 tim=1478672879417964

WAIT
#2: nam='db file scattered read' ela= 9 file#=4 block#=3563
blocks=5 obj#=81680 tim=1478672879418008

FETCH
#2:c=1000,e=270,p=10,cr=12,cu=0,mis=0,r=1,dep=0,og=1,plh=1931801113,tim=1478672879418046

WAIT
#2: nam='SQL*Net message from client' ela= 118 driver
id=1650815232 #bytes=1 p3=0 obj#=81680 tim=1478672879418186

WAIT
#2: nam='SQL*Net message to client' ela= 0 driver id=1650815232
#bytes=1 p3=0 obj#=81680 tim=1478672879418213

FETCH
#2:c=0,e=18,p=0,cr=1,cu=0,mis=0,r=9,dep=0,og=1,plh=1931801113,tim=1478672879418225

STAT
#2 id=1 cnt=10 pid=0 pos=1 obj=0 op='COUNT STOPKEY (cr=13 pr=10
pw=0 time=0 us)'

STAT
#2 id=2 cnt=10 pid=1 pos=1 obj=81680 op='INDEX FAST FULL SCAN
IND_TEST_ID (cr=13 pr=10 pw=0 time=0 us cost=2 size=40 card=10)'

WAIT
#2: nam='SQL*Net message from client' ela= 239 driver
id=1650815232 #bytes=1 p3=0 obj#=81680 tim=1478672879418502

***
SESSION ID:(1.13) 2016-11-09 14:27:59.419

结论:FFS会读取
段头块,并且会多块读

最开始扫描的是3562,它是索引的段头,并且是单块读(注意:段头都是单块读),然后才是从3563
开始扫描,一共扫描了5个block
3563就是索引的root
block

8.2FS(INDEX
FULL SCAN)

SQL>
set arraysize 1000;

SQL>
alter system flush buffer_cache; ----一定要刷新,不然观察不到
db file sequential
read

SQL>
alter system flush shared_pool;

SQL>
alter session set events '10046 trace name context forever, level 8';

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

OBJECT_ID

----------

10616

12177

12178

12179

12301

13495

13536

13539

13923

16503

10
rows selected.

SQL>
alter session set events '10046 trace name context off';

以下内容取自
10046 event trace文件

=====================

PARSING
IN CURSOR #4 len=72 dep=0 uid=88 oct=3 lid=88 tim=1478673548236909
hv=2159188642 ad='cf9c1348' sqlid='344baf60b56p2'

select/*+
index(test ind_TEST_ID)*/ object_id from test where rownum<11

END
OF STMT

PARSE
#4:c=27996,e=28261,p=17,cr=61,cu=0,mis=1,r=0,dep=0,og=1,plh=2443641574,tim=1478673548236908

EXEC
#4:c=0,e=15,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2443641574,tim=1478673548236966

WAIT
#4: nam='SQL*Net message to client' ela= 2 driver id=1650815232
#bytes=1 p3=0 obj#=0 tim=1478673548237005

WAIT
#4: nam='db file sequential read' ela= 10 file#=4 block#=3563
blocks=1 obj#=81680 tim=1478673548237648 --直接跳过
3562数据块(也就是跳过段头块)

WAIT
#4: nam='db file scattered read' ela= 31 file#=4 block#=3564
blocks=4 obj#=81680 tim=1478673548237730 读取3564数据块
一次读入4个数据块

FETCH
#4:c=1000,e=735,p=5,cr=2,cu=0,mis=0,r=1,dep=0,og=1,plh=2443641574,tim=1478673548237758

WAIT
#4: nam='SQL*Net message from client' ela= 124 driver
id=1650815232 #bytes=1 p3=0 obj#=81680 tim=1478673548237914

WAIT
#4: nam='SQL*Net message to client' ela= 1 driver id=1650815232
#bytes=1 p3=0 obj#=81680 tim=1478673548237949

FETCH
#4:c=0,e=22,p=0,cr=1,cu=0,mis=0,r=9,dep=0,og=1,plh=2443641574,tim=1478673548237962

STAT
#4 id=1 cnt=10 pid=0 pos=1 obj=0 op='COUNT STOPKEY (cr=3 pr=5 pw=0
time=0 us)'

STAT
#4 id=2 cnt=10 pid=1 pos=1 obj=81680 op='INDEX FULL SCAN
IND_TEST_ID (cr=3 pr=5 pw=0 time=0 us cost=2 size=40 card=10)'

WAIT
#4: nam='SQL*Net message from client' ela= 193 driver
id=1650815232 #bytes=1 p3=0 obj#=81680 tim=1478673548238201

***
SESSION ID:(1.13) 2016-11-09 14:39:08.239

结论:这个索引的段头块是3562,root
block就是段头+1
,这里 root
block 就是3563
,根据实验可知,index
full scan 没有扫描 segment
header ,而是直接扫描 root
block3563、leaf
block 3564

结论:两者的结果完全不一样,这是为什么呢?

这是因为当进行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个块

9.原因考证

归纳:

索引类别

访问方式

是否排序

FFS

先扫描 segment
header,读取索引的段头,然后开始读取
root block、brunch
block、leaf
block

多一步 sort
(order by)

FS

不扫描 segment
header, 跳过索引的段头,而是直接扫描
root block、brunch
block、leaf
block

自动的执行 sort
(order by)

详情

为什么
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 扫描完了没,它只需要从左往右,或者从右往左一直扫描到尽头即可。

index ffs、index fs原理考究-1109的更多相关文章

  1. 【从翻译mos文章】采用高速全扫描索引(index ffs) 为了避免全表扫描

    采用高速全扫描索引(index ffs) 为了避免全表扫描 参考原始: Index Fast Full Scan Usage To Avoid Full Table Scans (Doc ID 701 ...

  2. index index.html index.htm index.php

    server { listen 80; server_name localhost; index index.html index.htm index.php;#前后顺序有关系,越在前优先级别越高 r ...

  3. nginx -t "nginx: [warn] only the last index in "index" directive should be absolute in 6 "的问题解决

    修改完nginx的配置文件之后,执行nginx -t命令提示"nginx: [warn] only the last index in "index" directive ...

  4. 14.8.11 Physical Structure of an InnoDB Index InnoDB Index 的物理结构

    14.8.11 Physical Structure of an InnoDB Index InnoDB Index 的物理结构 所有的InnoDB indexes 是 B-trees Index r ...

  5. 14.2.5.4 Physical Structure of an InnoDB Index InnoDB Index 的物理结构

    14.2.5.4 Physical Structure of an InnoDB Index InnoDB Index 的物理结构 所有的InnoDB indexes 是B-trees ,index ...

  6. MySQL 执行计划中Extra(Using where,Using index,Using index condition,Using index,Using where)的浅析

      关于如何理解MySQL执行计划中Extra列的Using where.Using Index.Using index condition,Using index,Using where这四者的区别 ...

  7. [Oacle][Partition]Partition操作与 Index, Global Index 的关系

    [Oacle][Partition]Partition操作与 Index, Global Index 的关系: ■ Regarding the local index and the global i ...

  8. C 缓冲区过读 if (index >= 0 && index < len)

    C 缓冲区过读 if (index >= 0 && index < len) CWE - CWE-126: Buffer Over-read (3.2) http://cw ...

  9. Sql Server中的表访问方式Table Scan, Index Scan, Index Seek

    1.oracle中的表访问方式 在oracle中有表访问方式的说法,访问表中的数据主要通过三种方式进行访问: 全表扫描(full table scan),直接访问数据页,查找满足条件的数据 通过row ...

随机推荐

  1. JSch - Java实现的SFTP(文件上传详解篇)(转)

    JSch是Java Secure Channel的缩写.JSch是一个SSH2的纯Java实现.它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等,当然你也可以集成它的功能到 ...

  2. Java项目中使用配置文件配置

    private String readConfig() { Properties p = new Properties(); InputStream in = getClass().getClassL ...

  3. 【转】android 完全退出应用程序

    原文网址:http://www.yoyong.com/archives/199 android退出应用程序会调用android.os.Process.killProcess(android.os.Pr ...

  4. 贪心(哈夫曼树):HDU 5884 sort

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAA2QAAAKACAIAAAB8KCy/AAAgAElEQVR4nOy9a5Adx3UmWL+kHxuekU ...

  5. vijosP1067Warcraft III 守望者的烦恼

    vijosP1067Warcraft III 守望者的烦恼 链接:https://vijos.org/p/1067 [思路] 矩阵乘法. 可以得出递推式:      f[i]=sum{ f[n-1], ...

  6. Java程序员必知的8大排序算法

    8种排序之间的关系 直接插入排序 (1)基本思想:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排 好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数 也是排好顺序的.如 ...

  7. Intersection - POJ 1410(线段与矩形是否相交)

    题目大意:给一个线段和一个矩形,判断线段是否和矩形有公共点.   分析:用矩形的四个边当线段判断与所给的线段是否有交点,需要注意的是给的矩形是不标准的,需要自己转换,还需要注意线段有可能在矩形内部. ...

  8. JVM分代垃圾回收策略的基础概念

    由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略.本文介绍了分代策略的目标,如何分代,以及垃圾回收的触发因素. 文章总结了JVM垃圾回收策略为什么要分代,如何分代,以及垃圾回收 ...

  9. HIVE自定义函数 UDF

    自定义my_md5hash 具体hive源码怎么调用看我另外一篇博客 package udf.hive.myudf; import org.apache.commons.codec.digest.Di ...

  10. 【javascript基础知识】javascript中的转义序列和特殊数值常量

    javascript的转义序列 \0 NUL字符(\u0000) \b 退格符(\u0008) \t 水平制表符(\u0009) \n 换行符(\u000A) \v 垂直制表符(\u000B) \f ...