index ffs、index fs原理考究-1109
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 扫描完了没,它只需要从左往右,或者从右往左一直扫描到尽头即可。 |
转载于:https://www.cnblogs.com/iyoume2008/p/6051100.html
index ffs、index fs原理考究-1109相关推荐
- index ffs、index fs原理考究
1.创建测试表 SQL> CREATE TABLE TEST AS SELECT * FROM dba_objects WHERE 0=1; 2.创建测试索引 SQL> CREATE IN ...
- oracle查询不走索引全表扫描,使用索引快速全扫描(Index FFS)避免全表扫描的若干场景-Oracle...
使用索引快速全扫描(Index FFS)避免全表扫描的若干场景 什么使用使用Index FFS比FTS好? Oracle 8的Concept手册中介绍: 1. 索引必须包含所有查询中参考到的列. 2. ...
- oracle 索引快速全扫描,使用索引快速全扫描(Index FFS)避免全表扫描的若干场景
使用索引快速全扫描(Index FFS)避免全表扫描(FTS) (文档 ID 70135.1) 什么使用使用Index FFS比FTS好? Oracle 8的Concept手册中介绍: 1. 索引必须 ...
- oracle 索引快速全扫描,使用目录快速全扫描(Index FFS)避免全表扫描的若干场景
使用索引快速全扫描(Index FFS)避免全表扫描的若干场景 使用索引快速全扫描(Index FFS)避免全表扫描(FTS) (文档 ID 70135.1) 什么使用使用Index FFS比FTS好 ...
- mysql ignore index,mysql强制索引FORCE INDEX/IGNORE INDEX忽略索引
FORCE INDEX 通常用来对查询强制使用一个或者多个索引. MySQL 通常会根据统计信息选择正确的索引,但是当查询优化器选择了错误的索引或者根本没有使用索引的时候,这个提示将非常有用. IGN ...
- :/index.php,http://localhost/my/INDEX.PHP/INDEX/INDEX无法正常运行:解决时找不到Options FollowSymLinks谢谢...
源自:2-3 路由类 http://localhost/my/INDEX.PHP/INDEX/INDEX无法正常运行:解决时找不到Options FollowSymLinks谢谢 打不到: Optio ...
- url(r'^index/$',views.index)的含义解释
已知: urls.py中的代码是: from django.contrib import admin from django.urls import path from django import u ...
- php_self include,PHP_SELF返回/index.php/index.php
为什么$_SERVER ['PHP_SELF']会返回/index.php/index.php ?? 请求 http://example.com 产量 /index.php/index.php 的in ...
- Python中[index for index, value in enumerate(a) if value > 3]
并不是index for index,这段代码应该这么看.(大括号内视为一个整体) [{index} for {index, value} in {enumerate(x)} if {value == ...
最新文章
- linux文件描述符
- Linux中的文件描述符与打开文件之间的关系
- Environment.CurrentDirectory 的一个坑
- 操作系统学习笔记-02-1.2-什么是操作系统
- (回溯 UVa129)困难的串
- Python之eval函数实例解释
- 操作系统已经向SQL Server 返回了错误21
- webpack2.7.0配置不同的打包环境
- 在Visual Studio 2005中调试SQL Server 2005的存储过程 (转)
- paip.提升开发效率-----vs2010 快速查找文件
- 论文笔记 -- Contrastive Clustering(对比聚类)
- MT9 二维数组打印(Python)
- 2019冬令营集训1月7-10日总结
- [论文阅读笔记]DeepFool: a simple and accurate method to fool deep neural networks
- GAC注册/卸载 dll
- JavaScript 扁平化数组转成Tree
- xp网络发现不了自己的计算机,xp系统“网络发现”功能启用不了的方案介绍
- 行业资讯 | 深圳:BIM法定化,开历史之先河
- 直接插入法(java实现)
- jq的ajax上传文件