oracle数据库下,索引在做完统计分析后,会获得很多重要信息,其中之一就是cluster_factor,cluster_factor表示索引数据顺序和表数据顺序的一致性,关于cluster_factor的理论和机制分析见随后作者的文章,oracle高级sql调优之:cluster_factor机制研究。
cluster_factor的精彩之处就在于,能借此区分看来貌似完全相同的情况:表结构、表数据和索引完全相同,但就是表数据行的存储顺序不同。下面以案例的形式加以分析。
1. 研究结论
cluster_factor对oracle执行计划会产生重要影响。这个值越高,说明索引的使用效率将会越差。这个值会存在于一个区间内,区间的最小值为表占用的数据块数,最大值为表拥有的数据行数。
2. 研究对象
研究对象为两个数据表testcf和testcf2,两者的数据结构相同,都只有两列:id列,整数型,name列,字符型,80个字符,数据相同,都8万行,占用1024个数据块。不同的在于两个表的数据行的存储顺序不同,testcf表的数据,按照id值从小到大的顺序依次存储,而testcf2表的数据,随机杂乱存储。
3. 案例实验过程
3.1 系统配置:
oracle 11.1.0.6,初始化参数optimizer_index_cost_adj为默认值100
sql> select from v$version
banner
--------------------------------------------------------------------------------
oracle database 11g enterprise edition release 11.1.0.6.0 - production
pl/sql release 11.1.0.6.0 - production
core 11.1.0.6.0 production
tns for 32-bit windows: version 11.1.0.6.0 - production
nlsrtl version 11.1.0.6.0 – production
sql> select name type value from v$parameter p where p.name = ’optimizer_index_cost_adj’
name type value
------------------------------ ---------- --------------------
optimizer_index_cost_adj 3 100 3.2 创建表testcf和testcf2
设置表tescf控制其行长度和行数使得总共占用约1024个数据块
表定义:每行至少80个字节,共8万行,pctfree = 0,初始盘区和next盘区都为1m
3.2.1 创建表testcf,并产生数据
数据的两列,分别由类序列值和随机函数产生,随机函数直接产生80位长的字符
sql> create table testcf
2 (
3 id number(32)
4 name varchar2(80)
5 )
6 tablespace users
7 pctfree 0
8 initrans 1
9 maxtrans 255
10 storage
11 (
12 initial 1m
13 next 1m
14 minextents 1
15 maxextents unlimited
16 )
表已创建。
已用时间: 00: 00: 00.06
sql> begin
2 for i in 1..80000 loop
3 insert into testcf(id name)
4 values(i dbms_random.string(’a’ 80))
5 end loop
6 commit
7 end
8 /
pl/sql 过程已成功完成。
已用时间: 00: 00: 18.53 3.2.2 创建表testcf2
该表和表testcf结构相同,且数据相同。将testcf表的所有数据灌入testcf2以获得相应信息。
重要:灌入时,让数据随机的进入testcf2,由dbms_random控制员vぷ愎坏乃婊浴?br> sql> create table testcf2
2 (
3 id number(32)
4 name varchar2(80)
5 )
6 tablespace users
7 pctfree 0
8 initrans 1
9 maxtrans 255
10 storage
11 (
12 initial 1m
13 next 1m
14 minextents 1
15 maxextents unlimited
16 )
表已创建。
已用时间: 00: 00: 00.04
sql>
sql> insert into testcf2 nologging
2 select from testcf order by dbms_random.random
已创建80000行。
已用时间: 00: 00: 01.28
sql>
sql> commit
提交完成。
已用时间: 00: 00: 00.00 3.3 给两个表都创建pk
sql> alter table testcf add constraint pk_testcf primary key(id)
表已更改。
已用时间: 00: 00: 00.71
sql>
sql> alter table testcf2 add constraint pk_testcf2 primary key(id)
表已更改。
已用时间: 00: 00: 00.37
表都为1024个数据块,索引都为256个数据块。
sql> select t.segment_name t.segment_type t.blocks from user_segments t where t.segment_name like ’testcf’
segment_name segment_type blocks
-------------------- ------------------------------------ ----------
testcf2 table 1024
pk_testcf index 256
testcf table 1024
pk_testcf2 index 256
已用时间: 00: 00: 00.10
3.4 查看数据样例
sql> select from testcf where rownum < 3
id name
---------- --------------------------------------------------------------------------------
1 xmamnromseweamypdopxkesqzknbqrxloexsahigzirranrtzprtooawwooeimygjtwbuhwcxhplskky
2 aevffxiltlwjtgnjcotvuovwwgfhzkvxtjjokgrdftkonklzvignzfuxlanfhdimvgxdnfmhhejizhvs
已用时间: 00: 00: 00.00
sql> select from testcf2 where rownum < 3
id name
---------- --------------------------------------------------------------------------------
39312 uwnqugvticiholgfccbnivhouteszhvphwljeeydkukfuywcckikyrkpqsiunrjqyursqejnwmsdteqw
41453 cosqpzchzgbykhnjbhigbwuybkqucbrctnhbyhyvjdnitsxpxdkwxzsdyiksbujsuiziolellwpvosny
已用时间: 00: 00: 00.01 3.5分别产生统计数据,然后查看两个表pk索引的cluster_factor
可以看出两表pk索引的cluster_factor值相差甚远。
testcf表的值为908,接近于表的数据块数(1024),
testcf2表的值为79913,接近于表的数据行数(80000),
这说明,当数据行的存储顺序和索引顺序越接近,cluster_factor越小,越有利于使用索引。
sql> begin
2 dbms_stats.gather_table_stats(ownname => user tabname => ’testcf’ cascade => true)
3 end
4 /
pl/sql 过程已成功完成。
已用时间: 00: 00: 00.93
sql> begin
2 dbms_stats.gather_table_stats(ownname => user tabname => ’testcf2’ cascade => true)
3 end
4 /
pl/sql 过程已成功完成。
已用时间: 00: 00: 00.45
sql> select i.index_name i.clustering_factor from dba_indexes i where i.index_name like ’pk_testcf’
index_name clustering_factor
------------------------------ -----------------
pk_testcf 908
pk_testcf2 79913
3.6 测试cluster_factor对执行计划的影响
执行相同的查询语句,获得一段连续id内的数据行,以检查cluster_factor对执行计划的影响
从实验可以看出,cluster_factor对执行计划产生了巨大影响,这使得
a) testcf表的执行计划,走的是index range scan,而test2表走的是table access full
同事这导致testcf表的总cost为83,testcf2表的总cost为210,是后者的四倍。
b) testcf表的物理读和一致性读远小少于testcf2。
sql> alter system flush buffer_cache
系统已更改。
已用时间: 00: 00: 00.06
sql> set autotrace traceonly
sql> select from testcf where id > 2000 and id < 8000
已选择5999行。
已用时间: 00: 00: 00.65 执行计划
----------------------------------------------------------
plan hash value: 2216396729
--------------------------------------------------------------------------------------
| id | operation | name | rows | bytes | cost (cpu)| time
--------------------------------------------------------------------------------------
| 0 | select statement | | 6001 | 498k| 83 (0)| 00:00:0
| 1 | table access by index rowid| testcf | 6001 | 498k| 83 (0)| 00:00:0
| 2 | index range scan | pk_testcf | 6001 | | 14 (0)| 00:00:0
--------------------------------------------------------------------------------------
predicate information (identified by operation id):
---------------------------------------------------
2 - access(" id" > 2000 and " id" < 8000)
统计信息
----------------------------------------------------------
320 recursive calls
0 db block gets
929 consistent gets
121 physical reads
0 redo size
592212 bytes sent via sqlnet to client
4774 bytes received via sqlnet from client
401 sqlnet roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
5999 rows processed
sql> alter system flush buffer_cache
系统已更改。
已用时间: 00: 00: 00.03
sql> set autotrace traceonly
sql> select from testcf2 where id > 2000 and id < 8000
已选择5999行。
已用时间: 00: 00: 00.59 执行计划
----------------------------------------------------------
plan hash value: 4178501150
-----------------------------------------------------------------------------
| id | operation | name | rows | bytes | cost (cpu)| time |
-----------------------------------------------------------------------------
| 0 | select statement | | 6001 | 498k| 210 (2)| 00:00:03 |
| 1 | table access full| testcf2 | 6001 | 498k| 210 (2)| 00:00:03 |
-----------------------------------------------------------------------------
predicate information (identified by operation id):
---------------------------------------------------
1 - filter(" id" < 8000 and " id" > 2000)
统计信息
----------------------------------------------------------
312 recursive calls
0 db block gets
1383 consistent gets
944 physical reads
0 redo size
568273 bytes sent via sqlnet to client
4774 bytes received via sqlnet from client
401 sqlnet roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
5999 rows processed
sql>