这篇文章主要介绍“什么是Clustering Factor”,在日常操作中,相信很多人在什么是Clustering Factor问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是Clustering Factor”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、什么是聚簇因子

Clustering Factor(CF)聚簇因子,意味着对索引的表的群集度,用来比较索引的顺序程度与表的杂乱排序程度的一个度量。

集群因子用于判断索引回表需要消耗的物理 I/O 次数。

CF是在假设内存大小只能载入一个块,这是伴随索引扫描所需要的表扫描次数的计算值。

更准确地说,他表示沿着索引的叶块,在ROWID值上代表块编号的第1~15位的值与之前ROWID作出比较后被更替的次数。

如:

有五个块组成的索引和由五个块组成的表,而一个块里有4行记录,那么总行数就是20行。

顺序的扫描索引的同时读取相对应的表,这时可能有两个极端情况:

1、CF最低时

如果一个索引块所包含的ROWID都被一个表块所包含,则通过索引扫描表时,只扫描索引5次和表5次,就可以得到想要的数据。

此时CF=5(表块的扫描次数),CF的最小值与表的块数相同

2、CF最高时

如果一个索引块所包含的ROWID分别被不同的表块所包含,则通过索引扫描表时,

必须扫描20次[4(索引块数)+4(索引块数)+4(索引块数)+4(索引块数)+4(索引块数) = 20次]才能获得想要的数据。

这时CF=20(表块的扫描次数),CF最大值与表的行数相同

二、聚簇因子计算过程

聚簇因子大致的计算方法顺序如下:

1、进行一次索引全扫描

2、检查索引块中的rowid信息。比较与前一个rowid是否指向同一个数据块。若不同,则聚簇因子加1.

3、当完成整个的索引扫面后,即得到该索引的聚簇因子的数值。

计算过程

SELECT*FROM(SELECTID,ROWIDFROMTMP_CFORDERBYID)WHEREROWNUM<=5;IDROWID----------------------------2AAAVqxAAEAAAAfDACL3AAAVqxAAEAAAAfDAAf3AAAVqxAAEAAAAfDAJR4AAAVqxAAEAAAAfDAGZ4AAAVqxAAEAAAAfDAKP

首先我们比较 2、3 对应的 ROWID 是否在同一个数据块,如果在同一个数据块,Clustering Factor +0;

如果不在同一个数据块,那么 Clustering Factor 值加 1。

然后我们比较 3、4 对应的 ROWID 是否在同一个数据块,如果在同一个数据块,Clustering Factor 值不变;

如果不在同一个数据块,那么 Clustering Factor 值加 1。

接下来我们比较4、5对应的ROWID是否在同一个数据块,如果在同一个数据块,Clustering Factor +0;

如果不在同一个数据块,那么 Clustering Factor 值加 1。

像上面步骤一样,一直这样有序地比较下去,直到比较完索引中最后一个键值。

表中的数据块数 <= 聚簇因子的取值范围 <= 表中的记录数

三、聚簇因子的影响

如果集群因子与块数接近,说明表的数据基本上是有序的,而且其顺序基本与索引顺序一样。

这样在进行索引范围或者索引全扫描的时候,回表只需要读取少量的数据块就能完成。

如果集群因子与表记录数接近,说明表的数据和索引顺序差异很大,在进行索引范围扫描或者索引全扫描的时候,回表会读取更多的数据块。

集群因子只会影响索引范围扫描(INDEX RANGE SCAN)以及索引全扫描(INDEX FULL SCAN),因为只有这两种索引扫描方式会有大量数据回表。

集群因子不会影响索引唯一扫描(INDEX UNIQUE SCAN),因为索引唯一扫描只返回一条数据。

集群因子更不会影响索引快速全扫描(INDEX FAST FULL SCAN),因为索引快速全扫描不回表。

集群因子影响的是索引回表的物理 I/O 次数。

我们假设索引范围扫描返回了 1 000 行数据,如果 buffer cache 中没有缓存表的数据块,

假设这 1000行数据都在同一个数据块中,那么回表需要耗费的物理 I/O 就只需要一个;

假设这 1000 行数据都在不同的数据块中,那么回表就需要耗费 1 000 个物理 I/O。

因此,集群因子影响索引回表的物理 I/O 次数。

假设I/O不使用内存,CF越高读取表块的次数就越多,因此物理I/O也会增加。

即,CF越高,通过ROWID读取表块的次数就会越多,db file sequential read也会相应的增加。

通过高速缓冲区读取过一次的块因为不在发生物理I/O,因此CF高未必一定会降低SQL语句的性能。

但是大量扫描CF值高的索引,则需要读取的表块增加,因此可能给性能带来严重问题。

比如当ORACLE计算索引范围扫描(IRS)及其回表时的成本时,

使用如下的公式:

IRS COST=I/O COST + CPU COST

其中 I/O COST = INDEX ACCESS I/O COST + TABLE ACCESS I/O COST

其中:

INDEX ACCESS I/O COST = BLEVEL+CEIL(#LEAF_BLOCKS*IX_SEL)

TABLE ACCESS I/O COST = CEIL(CLUSTERING_FACTOR*IX_SEL_WITH_FILTERS)

索引的聚簇因子越大

--》从该索引回表的成本越大

--》使用该索引的综合成本越大

--》使用该索引的可能越小

对索引收集统计信息,则索引的聚簇因子会被记录在 DBA_INDEXES的CLUSTING_FACTOR列。

如果CF与表的块数相当就是“好现象”;如果CF和表的行数相当就不是“好现象”。

四、如何优化

如果SQL语句的性能问题被判断位由于聚簇因子导致,如何进行优化?

1、将索引扫描换成全表扫描

2、使用其他索引

3、按照索引的排列顺序重新创建表(最后的选择)

create table new_table as select ... from old_table order by indexd_column

4、某些情况,设置table_cached_blocks参数

5、设置set_index_stats的聚簇因子值clstfct

注意:当索引范围扫描,索引全扫描不回表或者返回数据量很少的时候,不管集群因子多大,对SQL查询性能几乎没有任何影响。在进行SQL优化的时候,往往会建立合适的组合索引消除回表,或者建立组合索引尽量减少回表次数。五、聚簇因子对执行计划的影响的实验:

1、聚簇因子高对执行计划的影响

2、聚簇因子低对执行计划的影响

3、调整聚簇因子

3.1、根据索引列重新创建表

3.2、设置table_cached_blocks参数

3.3、设置set_index_stats的聚簇因子值clstfct

1、聚簇因子高对执行计划的影响

/*创建测试数据*//*为了制造出聚簇因子较高的索引,对生成的ID数值取随机数1至1000。*/SQL>createtabletmp_cfasselectceil(dbms_random.value*1000)id,'a'namefromdualconnectbyrownum<=1000;Tablecreated./*在ID列上创建索引*/SQL>createindexind_idontmp_cf(id);Indexcreated./*收集统计信息*/SQL>execdbms_stats.gather_table_stats('demo','tmp_cf');PL/SQLproceduresuccessfullycompleted./*开启autotrace查看执行计划*/SQL>setautottraceonly/*执行查询,条件为ID<=6*/SQL>select*fromtmp_cfwhereid<=6;ExecutionPlan----------------------------------------------------------Planhashvalue:1290879752----------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------|0|SELECTSTATEMENT||6|36|3(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF|6|36|3(0)|00:00:01|----------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=6)Statistics----------------------------------------------------------0recursivecalls0dbblockgets4consistentgets0physicalreads0redosize676bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)6rowsprocessed

/* 从执行计划中可见,在只返回6行的情况下,估算出的走索引并回表的成本仍然大于全表扫描。 */

/* 尝试改条件为 id<=5 ,id<=4,id<=3 进一步减少返回的行数。 */

SQL>select*fromtmp_cfwhereid<=5;ExecutionPlan----------------------------------------------------------Planhashvalue:1290879752----------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------|0|SELECTSTATEMENT||5|30|3(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF|5|30|3(0)|00:00:01|----------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=5)Statistics----------------------------------------------------------1recursivecalls0dbblockgets4consistentgets0physicalreads0redosize668bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)5rowsprocessedSQL>select*fromtmp_cfwhereid<=4;ExecutionPlan----------------------------------------------------------Planhashvalue:1290879752----------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------|0|SELECTSTATEMENT||4|24|3(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF|4|24|3(0)|00:00:01|----------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=4)Statistics----------------------------------------------------------1recursivecalls0dbblockgets4consistentgets0physicalreads0redosize668bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)5rowsprocessedSQL>select*fromtmp_cfwhereid<=3;ExecutionPlan----------------------------------------------------------Planhashvalue:1290879752----------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------|0|SELECTSTATEMENT||3|18|3(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF|3|18|3(0)|00:00:01|----------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=3)Statistics----------------------------------------------------------1recursivecalls0dbblockgets4consistentgets0physicalreads0redosize652bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)3rowsprocessedSQL>select*fromtmp_cfwhereid<=2;ExecutionPlan----------------------------------------------------------Planhashvalue:2401189212--------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|--------------------------------------------------------------------------------------|0|SELECTSTATEMENT||2|12|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF|2|12|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID|2||2(0)|00:00:01|--------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=2)Statistics----------------------------------------------------------1recursivecalls0dbblockgets4consistentgets0physicalreads0redosize592bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)1rowsprocessed

/* 可见,当返回的行数为1行时,才选择了用索引并回表的访问路径。*/

2、聚簇因子低对执行计划的影响

/*创建测试数据*//*创建测试表,但这次ID列的值为顺序产生。*/SQL>createtabletmp_cf_2asselectrownumid,rpad(rownum,50,'a')col1fromdualconnectbyrownum<=1000;Tablecreated./*在ID列上创建索引*/SQL>createindexind_id_2ontmp_cf_2(id);Indexcreated./*执行查询,条件为ID<=6*/SQL>select*fromtmp_cf_2whereid<=6;ExecutionPlan----------------------------------------------------------Planhashvalue:1887041669----------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------------------|0|SELECTSTATEMENT||6|390|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF_2|6|390|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID_2|6||2(0)|00:00:01|----------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=6)Note------dynamicsamplingusedforthisstatement(level=2)Statistics----------------------------------------------------------10recursivecalls0dbblockgets23consistentgets1physicalreads0redosize986bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)6rowsprocessed

/* 从上面可见,此时已经采用了先索引再回表的访问路径,说明此时其计算出来的成本,是小于全表扫描的。*/

/* 继续执行查询,将条件依次为ID<=200,ID<=222,ID<=223,逐步增加返回的行数 */

SQL>select*fromtmp_cf_2whereid<=200;ExecutionPlan----------------------------------------------------------Planhashvalue:1887041669----------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------------------|0|SELECTSTATEMENT||200|13000|5(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF_2|200|13000|5(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID_2|200||3(0)|00:00:01|----------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=200)Note------dynamicsamplingusedforthisstatement(level=2)Statistics----------------------------------------------------------7recursivecalls0dbblockgets48consistentgets0physicalreads0redosize14949bytessentviaSQL*Nettoclient667bytesreceivedviaSQL*Netfromclient15SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)200rowsprocessedSQL>select*fromtmp_cf_2whereid<=222;ExecutionPlan----------------------------------------------------------Planhashvalue:1887041669----------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------------------|0|SELECTSTATEMENT||222|14430|5(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF_2|222|14430|5(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID_2|222||3(0)|00:00:01|----------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=222)Note------dynamicsamplingusedforthisstatement(level=2)Statistics----------------------------------------------------------7recursivecalls0dbblockgets50consistentgets0physicalreads0redosize16455bytessentviaSQL*Nettoclient678bytesreceivedviaSQL*Netfromclient16SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)222rowsprocessedSQL>select*fromtmp_cf_2whereid<=223;ExecutionPlan----------------------------------------------------------Planhashvalue:2467544485------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|------------------------------------------------------------------------------|0|SELECTSTATEMENT||223|14495|5(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF_2|223|14495|5(0)|00:00:01|------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=223)Note------dynamicsamplingusedforthisstatement(level=2)Statistics----------------------------------------------------------7recursivecalls0dbblockgets43consistentgets0physicalreads0redosize15679bytessentviaSQL*Nettoclient678bytesreceivedviaSQL*Netfromclient16SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)223rowsprocessed

/* 从上可见,当返回223行时,才开始采用全表扫描的方式。*/

/* 查看一下两个索引的聚簇因子 */

SQL>selectindex_name,clustering_factorfromuser_indexeswhereindex_namein('IND_ID','IND_ID_2');INDEX_NAMECLUSTERING_FACTOR-----------------------------------------------IND_ID443IND_ID_29

当索引的聚簇因子较高时,会导致该索引回表的成本成大,使得该索引的综合成本越大,因此使用该索引的可能性越低。

但是,如果在执行计划出现错误,使用了该索引,并且该索引的聚簇因子很大,那么通过索引回表的次数就会很大,同时会造成大量的db file sequential read等待事件。

3.1、根据索引列重新创建表

/* 根据tmp_cf的表索引列排完序在创建新表tmp_cf_3 */

SQL>createtabletmp_cf_3asselect*fromtmp_cforderbyid;Tablecreated./*创建索引*/SQL>createindexind_id_3ontmp_cf_3(id);Indexcreated./*执行查询id<=6*/SQL>select*fromtmp_cf_3whereid<=6;6rowsselected.ExecutionPlan----------------------------------------------------------Planhashvalue:2546856486----------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------------------|0|SELECTSTATEMENT||6|96|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF_3|6|96|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID_3|6||2(0)|00:00:01|----------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=6)Note------dynamicsamplingusedforthisstatement(level=2)Statistics----------------------------------------------------------10recursivecalls0dbblockgets15consistentgets1physicalreads0redosize692bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)6rowsprocessed

/* 从上面可见,此时已经采用了先索引再回表的访问路径,说明此时其计算出来的成本,是小于全表扫描的。*/

再次查看聚簇因子SQL>selectindex_name,clustering_factorfromuser_indexeswhereindex_namein('IND_ID','IND_ID_2','IND_ID_3');INDEX_NAMECLUSTERING_FACTOR-----------------------------------------------IND_ID443IND_ID_29IND_ID_32

根据索引列排完序创建的表,然后在原索引列创建索引,此时的聚簇因子很小。

3.2、设置table_cached_blocks参数

在设置该参数前,有一种方式可以在创建索引前先计算出该索引的聚簇因子。

/*创建测试数据*/SQL>createtablet1asselect*fromdba_objects;Tablecreated.计算聚簇因子(需要在object_name列创建索引)SELECTSUM(CASEWHENBLOCK#1=BLOCK#2ANDFILE#1=FILE#2THEN0ELSE1END)CLUSTERING_FACTORFROM(SELECTDBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)FILE#1,LEAD(DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID),1,NULL)OVER(ORDERBYOBJECT_NAME)FILE#2,DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)BLOCK#1,LEAD(DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID),1,NULL)OVER(ORDERBYOBJECT_NAME)BLOCK#2FROMT1WHEREOBJECT_NAMEISNOTNULL);或者/*更推荐使用下面*/SELECTSUM(COUNT_CHG)FROM(SELECTCASEWHENSUBSTRB(ROWID,1,15)<>LAG(SUBSTRB(ROWID,1,15),1,'000000000')OVER(ORDERBYOBJECT_NAME,ROWID)THEN1ELSE0ENDCOUNT_CHGFROMT1WHEREOBJECT_NAMEISNOTNULL);SUM(COUNT_CHG)--------------44076

/* 实际创建索引并且查看聚簇因子 */

SQL>createindext1_i1ont1(object_name);Indexcreated.SQL>selectindex_name,clustering_factorfromuser_indexeswheretable_name='T1'andindex_name='T1_I1';INDEX_NAMECLUSTERING_FACTOR-----------------------------------------------T1_I144076

/* 可以看到计算的聚簇因子的值和实际创建完索引的聚簇因子的值是一样的 */

参数 TABLE_CACHED_BLOCKS:

1、该参数自12C引入(11g也有效)。

2、告诉索引统计信息收集工具,假设索引所在的基表上有多少个数据块已经在内存中。

3、取值范围为1至255,默认值为1。

4、对小表上索引的聚簇因子影响大,而对于大表上索引的聚簇因子影响小。

/*接此前tmp_cf表的实验*//*查看ID列上索引的聚簇因子*/SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_ID1000443查看当前的TABLE_CACHED_BLOCKS的值SQL>selectdbms_stats.get_prefs('table_cached_blocks','DEMO','TMP_CF')fromdual;DBMS_STATS.GET_PREFS('TABLE_CACHED_BLOCKS','DEMO','TMP_CF')--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------1修改为16SQL>execdbms_stats.set_table_prefs('DEMO','TMP_CF','TABLE_CACHED_BLOCKS',16);PL/SQLproceduresuccessfullycompleted.SQL>selectdbms_stats.get_prefs('table_cached_blocks','DEMO','TMP_CF')fromdual;DBMS_STATS.GET_PREFS('TABLE_CACHED_BLOCKS','DEMO','TMP_CF')--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------16查看聚簇因子SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_ID1000443收集索引的统计信息SQL>execdbms_stats.gather_index_stats('DEMO','IND_ID');PL/SQLproceduresuccessfullycompleted.SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_ID10002

/*此时,再执行查询,条件为ID<=6 看此时的表现*/

SQL>setautottraceonlySQL>select*fromtmp_cfwhereid<=6;ExecutionPlan----------------------------------------------------------Planhashvalue:3869640664----------------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------------------------|0|SELECTSTATEMENT||6|330|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWIDBATCHED|TMP_CF|6|330|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID|6||2(0)|00:00:01|----------------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=6)Statistics----------------------------------------------------------0recursivecalls3dbblockgets18consistentgets0physicalreads0redosize876bytessentviaSQL*Nettoclient607bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)4rowsprocessed

/* 从执行计划中可见,原来走全表扫描,现在变成走索引范围扫描了。 */

/* 尝试改条件为 id<=100 ,id<=200,id<=300 逐步增大返回的行数。 看看何时不走索引了*/

SQL>select*fromtmp_cfwhereid<=100;110rowsselected.ExecutionPlan----------------------------------------------------------Planhashvalue:2401189212--------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|--------------------------------------------------------------------------------------|0|SELECTSTATEMENT||100|600|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF|100|600|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID|100||2(0)|00:00:01|--------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=100)Statistics----------------------------------------------------------1recursivecalls0dbblockgets59consistentgets0physicalreads0redosize3034bytessentviaSQL*Nettoclient601bytesreceivedviaSQL*Netfromclient9SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)110rowsprocessedSQL>select*fromtmp_cfwhereid<=200;206rowsselected.ExecutionPlan----------------------------------------------------------Planhashvalue:2401189212--------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|--------------------------------------------------------------------------------------|0|SELECTSTATEMENT||200|1200|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF|200|1200|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID|200||2(0)|00:00:01|--------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=200)Statistics----------------------------------------------------------1recursivecalls0dbblockgets118consistentgets0physicalreads0redosize5205bytessentviaSQL*Nettoclient667bytesreceivedviaSQL*Netfromclient15SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)206rowsprocessedSQL>select*fromtmp_cfwhereid<=300;286rowsselected.ExecutionPlan----------------------------------------------------------Planhashvalue:3869640664----------------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------------------------|0|SELECTSTATEMENT||300|16500|5(0)|00:00:01||1|TABLEACCESSBYINDEXROWIDBATCHED|TMP_CF|300|16500|5(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID|300||2(0)|00:00:01|----------------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=300)Statistics----------------------------------------------------------1recursivecalls0dbblockgets271consistentgets0physicalreads0redosize21347bytessentviaSQL*Nettoclient816bytesreceivedviaSQL*Netfromclient21SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)286rowsprocessedSQL>select*fromtmp_cfwhereid<=332;336rowsselected.ExecutionPlan----------------------------------------------------------Planhashvalue:2401189212--------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|--------------------------------------------------------------------------------------|0|SELECTSTATEMENT||333|1998|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF|333|1998|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_ID|333||2(0)|00:00:01|--------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=332)Statistics----------------------------------------------------------1recursivecalls0dbblockgets195consistentgets0physicalreads0redosize8307bytessentviaSQL*Nettoclient766bytesreceivedviaSQL*Netfromclient24SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)336rowsprocessedSQL>select*fromtmp_cfwhereid<=333;339rowsselected.ExecutionPlan----------------------------------------------------------Planhashvalue:1290879752----------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|----------------------------------------------------------------------------|0|SELECTSTATEMENT||334|2004|3(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF|334|2004|3(0)|00:00:01|----------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=333)Statistics----------------------------------------------------------1recursivecalls0dbblockgets26consistentgets0physicalreads0redosize7592bytessentviaSQL*Nettoclient766bytesreceivedviaSQL*Netfromclient24SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)339rowsprocessed

/* 如上实验,我们可以看到,TABLE_CACHED_BLOCKS对聚簇因子的大小是有影响的。而聚簇因子变小后,CBO会更倾向于使用该索引,而不是全表扫描。*/

参数 TABLE_CACHED_BLOCKS是如何影响聚簇因子的计算过程的:

下面以设置TABLE_CACHED_BLOCKS为3来描述计算过程:

准备一个存储三条记录的数组,每条记录有两列,一列存储块ID,一列存储顺序号。

按顺序遍历索引,并依此顺序为每个索引条目提供一个顺序号。

从当前索引条目中提取块id并在数组中搜索匹配的块ID。

如果你找到匹配的,就用当前的顺序号更新数组的顺序号;

如果没有找到匹配项,那么用当前块ID和顺序号替换数组中顺序号最小的记录,并增加clustering_factor的计数。

/*创建测试用表*//*为了构造一个行数为10行,但会占用多个数据块的表,所以,我们把其中一个列C1,用长度为2000个字节的字符填充。同时,ID列中的值也是随机的,非顺序的。*/SQL>createtabletmp_cf_4asselectceil(dbms_random.value(1,10))id,lpad('x',2000,'x')c1fromdualconnectbyrownum<=10;Tablecreated./*在ID列上创建索引*/SQL>createindexind_tmp_cf_4_idontmp_cf_4(id)tablespacedemo;Indexcreated./*查看此时该索引的聚簇因子值*/SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF_4'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_TMP_CF_4_ID108/*设置测试表TMP_CF_4上的TABLE_CACHED_BLOCKS的参数为3后,收集索引上的统计信息,并查看其聚簇因子的值*/SQL>execdbms_stats.set_table_prefs('DEMO','TMP_CF_4','TABLE_CACHED_BLOCKS',3);PL/SQLproceduresuccessfullycompleted.SQL>execdbms_stats.gather_index_stats('DEMO','IND_TMP_CF_4_ID');PL/SQLproceduresuccessfullycompleted.SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF_4'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_TMP_CF_4_ID105/*此时,我们可以看到其聚簇因子的值由此前的8变小到了5.*//*下面,我们来看一下,这个新值是如何计算出来的?*//*按ID列在索引中的顺序显示其值,顺序号以及对应行所在的块号*/SELECTROWNUMRN,ID,BLOCK_NUMFROM(SELECTID,DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)BLOCK_NUMFROMTMP_CF_4ORDERBYID,ROWID);RNIDBLOCK_NUM------------------------------1248592348623448604548615648596648597648618848609848601010486110rowsselected./*打开一个EXCEL表,模拟一个存储3行记录的数组,并计算聚簇因子的值*/RNIDBLOCK_NUM块ID顺序号------------------------------1248591048612348626485934486094860454861564859664859764861884860CF598486010104861

3.3、设置set_index_stats的聚簇因子值clstfct

exec dbms_stats.set_index_stats

('DEMO', #索引的属主

'IND_TAB_CACHED_1_ID', #索引名

clstfct=>100 #聚簇因子值

);

注:设置后,要把该索引所在的基表统计信息加锁

(dbms_stats.lock_table_stats)。以避免下次收集统计时,被新值覆盖。(dbms_stats包无单独针对索引统计信息加锁的方法)

/*创建测试数据*//*为了制造出聚簇因子较高的索引,对生成的ID数值取随机数1至1000。*/SQL>createtabletmp_cf_5asselectceil(dbms_random.value*1000)id,'a'namefromdualconnectbyrownum<=1000;Tablecreated./*在ID列上创建索引*/SQL>createindexind_tmp_cf_5_idontmp_cf_5(id);Indexcreated./*收集统计信息*/SQL>execdbms_stats.gather_table_stats('demo','tmp_cf_5');PL/SQLproceduresuccessfullycompleted./*开启autotrace查看执行计划*/SQL>select*fromtmp_cf_5whereid<=6;ExecutionPlan----------------------------------------------------------Planhashvalue:4131497868------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|------------------------------------------------------------------------------|0|SELECTSTATEMENT||7|42|3(0)|00:00:01||*1|TABLEACCESSFULL|TMP_CF_5|7|42|3(0)|00:00:01|------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------1-filter("ID"<=6)Statistics----------------------------------------------------------1recursivecalls0dbblockgets4consistentgets0physicalreads0redosize668bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)5rowsprocessed当前的索引的聚簇因子SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF_5'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_TMP_CF_5_ID1000438修改当前的聚簇因子值SQL>execdbms_stats.set_index_stats('DEMO','IND_TMP_CF_5_ID',clstfct=>10);PL/SQLproceduresuccessfullycompleted.SQL>selectindex_name,num_rows,clustering_factorfromdba_ind_statisticswheretable_name='TMP_CF_5'andtable_owner='DEMO';INDEX_NAMENUM_ROWSCLUSTERING_FACTOR---------------------------------------------------------IND_TMP_CF_5_ID100010再次查看执行计划SQL>select*fromtmp_cf_5whereid<=6;ExecutionPlan----------------------------------------------------------Planhashvalue:4231491781-----------------------------------------------------------------------------------------------|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|-----------------------------------------------------------------------------------------------|0|SELECTSTATEMENT||7|42|3(0)|00:00:01||1|TABLEACCESSBYINDEXROWID|TMP_CF_5|7|42|3(0)|00:00:01||*2|INDEXRANGESCAN|IND_TMP_CF_5_ID|7||2(0)|00:00:01|-----------------------------------------------------------------------------------------------PredicateInformation(identifiedbyoperationid):---------------------------------------------------2-access("ID"<=6)Statistics----------------------------------------------------------0recursivecalls0dbblockgets4consistentgets0physicalreads0redosize668bytessentviaSQL*Nettoclient524bytesreceivedviaSQL*Netfromclient2SQL*Netroundtripsto/fromclient0sorts(memory)0sorts(disk)5rowsprocessed

/* 从上面可见,此时已经采用了先索引再回表的访问路径,说明此时其计算出来的成本,是小于全表扫描的。*/

到此,关于“什么是Clustering Factor”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!