Oracle索引聚簇因子的含义及重要性

索引统计信息中需要我们最为重点关注的是CLUSTERING_FACTOR(聚簇因子)。

在Oracle数据库中,聚簇因子是指按照索引键值排序的索引行和存储于对应表中数据行的存储顺序和相似度。Oracle是按照如下的算法来计算聚簇因子的值:

  1. 聚簇因子的初始值为1。
  2. Oracle首先定位到目标索引处于最左边的叶子块。
  3. 从最左边的叶子块的第一个索引键值所在的索引行开始顺序扫描,在顺序扫描的过程中,Oracle会比对当前索引行的rowid和它之前的那个索引行(它们是相邻的关系)的rowid,如果这两个rowid并不是指向同一个表块,那么Oracle就将聚簇因子的当前值递增1;如果这两个rowid是指向同一个表块,Oracle就不改变聚簇因子的当前值。注意,这里Oracle在比对rowid时不需要回表去访问相应的表块。
  4. 上述比对rowid的过程会一直持续下去,直到顺序扫描完目标索引所有叶子块里的所有索引行。
  5. 上述顺序扫描操作完成后,聚簇因子的当前值就是索引统计信息中的CLUSTERING_FACTOR,Oracle会将其存储在数据字典里。

从上述聚簇因子的算法可以知道,如果聚簇因子的值接近对应表的表块的数量,则说明目标索引索引行和存储于对应表中数据行的存储顺序相似程度非常高。这也就意味着Oracle走索引范围扫描后取得目标rowid再回表去访问对应表块的数据时,相邻的索引行所对应的rowid极有可能处于同一个表块中,即Oracle在通过索引行记录的rowid回表第一次读取对应的表块并将该表块缓存在buffer cache中后,当再通过相邻索引行记录的rowid回表第二次读取对应的表块时,就不需要再产生物理I/O了,因为这次要访问的和上次已经访问过的表块是同一个块,Oracle已经将其缓存在了buffer
cache中。而如果聚簇因子的值接近对应表的记录数,则说明目标索引索引行和存储于对应表中数据行的存储顺序和相似程度非常低,这也就意味着Oracle走索引范围扫描取得目标rowid再回表去访问对应表块的数据时,相邻的索引行所对应的rowid极有可能不处于同一个表块中,即Oracle在通过索引行记录的rowid回表第一次去读取对应的表块并将表块缓存在buffer cache中后,当再通过相邻索引行记录的rowid回表第二次读取对应的表块时,还需要再产生物理I/O,因为这次要访问的和上次已经访问过的表块并不是同一个块。

换句话说,聚簇因子高的索引走索引范围扫描时比相同条件下聚簇因子低的索引要耗费更多的物理I/O,所以聚簇因子高的索引走索引范围扫描的成本会比相同条件下聚簇因子低的索引走索引范围扫描的成本高。

这里构造一个非常极端的例子,全索引中没有任何相邻的索引行记录的rowid指向表中相同的数据块:

根据上述聚簇因子的算法,我们可以算出此索引IDX_T1的聚簇因子的值应是20。

[email protected]>create table t1 (id number,name char(1200));

Table created.

[email protected]>insert into t1 values(1,‘1‘);

1 row created.

[email protected]>insert into t1 values(3,‘3‘);

1 row created.

[email protected]>insert into t1 values(5,‘5‘);

1 row created.

[email protected]>insert into t1 values(7,‘7‘);

1 row created.

[email protected]>insert into t1 values(9,‘9‘);

1 row created.

[email protected]>insert into t1 values(11,‘11‘);

1 row created.

[email protected]>insert into t1 values(13,‘13‘);

1 row created.

[email protected]>insert into t1 values(15,‘15‘);

1 row created.

[email protected]>insert into t1 values(17,‘17‘);

1 row created.

[email protected]>insert into t1 values(19,‘19‘);

1 row created.

[email protected]>insert into t1 values(2,‘2‘);

1 row created.

[email protected]>insert into t1 values(4,‘4‘);

1 row created.

[email protected]>insert into t1 values(6,‘6‘);

1 row created.

[email protected]>insert into t1 values(8,‘8‘);

1 row created.

[email protected]>insert into t1 values(10,‘10‘);

1 row created.

[email protected]>insert into t1 values(12,‘12‘);

1 row created.

[email protected]>insert into t1 values(14,‘14‘);

1 row created.

[email protected]>insert into t1 values(16,‘16‘);

1 row created.

[email protected]>insert into t1 values(18,‘18‘);

1 row created.

[email protected]>insert into t1 values(20,‘20‘);

1 row created.

[email protected]>commit;

Commit complete.

[email protected]>create index idx_t1 on t1(id);

Index created.

[email protected]>col location for a10
[email protected]>select id,dbms_rowid.rowid_relative_fno(rowid)||‘_‘||dbms_rowid.rowid_block_number(rowid) location from t1 order by location,id;

        ID LOCATION
---------- ----------
         1 4_300
         3 4_300
         5 4_300
         7 4_300
         9 4_300
        11 4_301
        13 4_301
        15 4_301
        17 4_301
        19 4_301
         2 4_302
         4 4_302
         6 4_302
         8 4_302
        10 4_302
        12 4_303
        14 4_303
        16 4_303
        18 4_303
        20 4_303

20 rows selected.

从上述显示结果可以看出1、3、5、7、9在4号文件的300号数据块内,11、13、15、17、19在4号文件的301号数据块内,2、4、6、8、10在4号文件的第302号数据块内,12、14、16、18、20在4号文件的第303号数据块内。

收集统计信息并查看聚簇因子的值

#收集统计信息并查看聚簇因子的值
[email protected]>exec dbms_stats.gather_table_stats(ownname=>‘ZX‘,tabname=>‘T1‘,method_opt=>‘for all columns size auto‘,cascade=>true,estimate_percent=>100);

PL/SQL procedure successfully completed.

[email protected]>select index_name,clustering_factor from dba_indexes where index_name=‘IDX_T1‘;

INDEX_NAME                                                                                 CLUSTERING_FACTOR
------------------------------------------------------------------------------------------ -----------------
IDX_T1                                                                                                    20

在Oracle数据库中,能够降低目标索引的聚簇因子的唯一方法就是对表中的数据按照目标索引的索引键值排序后重新存储。需要注意的是,这种按某一个目标索引的索引键值排序后重新存储表中数据的方法确实可以降低该目标索引聚簇因子的值 ,但可能会同时增加该表上存在的其他索引值的聚簇因子的值。

将表T1的数据原封不动的照搬到表T2中,只不过表T2的数据在存储时已经按id列排好序了

[email protected]>create table t2 as select * from t1 order by id;

Table created.

[email protected]>create index idx_t2 on t2(id);

Index created.

[email protected]>select id,dbms_rowid.rowid_relative_fno(rowid)||‘_‘||dbms_rowid.rowid_block_number(rowid) location from t2 order by location,id;

        ID LOCATION
---------- ----------
         1 4_171
         2 4_171
         3 4_171
         4 4_171
         5 4_171
         6 4_172
         7 4_172
         8 4_172
         9 4_172
        10 4_172
        11 4_173
        12 4_173
        13 4_173
        14 4_173
        15 4_173
        16 4_174
        17 4_174
        18 4_174
        19 4_174
        20 4_174

20 rows selected.

[email protected]>exec dbms_stats.gather_table_stats(ownname=>‘ZX‘,tabname=>‘T1‘,method_opt=>‘for all columns size auto‘,cascade=>true,estimate_percent=>100);

PL/SQL procedure successfully completed.

[email protected]>select index_name,clustering_factor from dba_indexes where index_name=‘IDX_T2‘;

INDEX_NAME                                                                                 CLUSTERING_FACTOR
------------------------------------------------------------------------------------------ -----------------
IDX_T2                                                                                                     4

重复与表T1相同的一系列的操作,从结果可以看出索引IDX_T2的聚簇因子降为了4。而相邻的数据也都在同一数据块中。

在Oracle数据库里,CBO在计算索引范围扫描(Index Range Scan)的成本时会使用如下公式:

IRS Cost = I/O Cost
+ CPU Cost

而I/O 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)

从这个公式可以推断走索引范围扫描的成本可以近似看作是和聚簇因子成正比,因此,聚簇因子值的大小实际上对CBO判断是否走相关的索引起着至关重要的作用。

演示一个例子,通过修改聚簇索引的值就让原本走索引范围扫描的执行计划变成了走全表扫描:

[email protected]>create table t1 as select * from dba_objects;

Table created.

[email protected]>create index idx_t1 on t1(object_id);

Index created.

[email protected]>select clustering_factor from dba_indexes where index_name=‘IDX_T1‘;

CLUSTERING_FACTOR
-----------------
             1063

[email protected]>select /*+ cluster_factor_expmple_1 */ object_id,object_name from t1 where object_id between 103 and 108;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
       103 MIGRATE$
       104 DEPENDENCY$
       105 ACCESS$
       106 I_DEPENDENCY1
       107 I_DEPENDENCY2
       108 I_ACCESS1

6 rows selected.

[email protected]>select * from table(dbms_xplan.display_cursor(null,null,‘all‘));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  ga3jv3kwwwmx5, child number 0
-------------------------------------
select /*+ cluster_factor_expmple_1 */ object_id,object_name from t1
where object_id between 103 and 108

Plan hash value: 50753647

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1     |     6 |   474 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_T1 |     6 |       |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
......省略部分输出

SQL走了索引范围扫描,成本值为3

使用Hint强制SQL走全表扫描:

[email protected]>select /*+ full(t1) */ object_id,object_name from t1 where object_id between 103 and 108;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
       103 MIGRATE$
       104 DEPENDENCY$
       105 ACCESS$
       106 I_DEPENDENCY1
       107 I_DEPENDENCY2
       108 I_ACCESS1

6 rows selected.

[email protected]>select * from table(dbms_xplan.display_cursor(null,null,‘all‘));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  b7hjwuvmg2ncy, child number 0
-------------------------------------
select /*+ full(t1) */ object_id,object_name from t1 where object_id
between 103 and 108

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |   287 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |     6 |   474 |   287   (1)| 00:00:04 |
--------------------------------------------------------------------------
......省略部分输出

现在SQL走全表扫描,成本值为287。

我们已经知道走索引范围扫描的成本可以近似看作是和聚簇因子成正比,所以如果想让上述SQL的执行计划从索引范围扫描变为全表扫描,那么只需要调整聚簇因子的值,使走索引范围扫描的成本值大于走全表扫描的成本值346即可达到目的。

先将索引IDX_T1的聚簇因子的值手工调整为100万:

[email protected]>exec dbms_stats.set_index_stats(ownname=>‘ZX‘,indname=>‘IDX_T1‘,clstfct=>1000000,no_invalidate=>false);

PL/SQL procedure successfully completed.

[email protected]>select clustering_factor from dba_indexes where index_name=‘IDX_T1‘;

CLUSTERING_FACTOR
-----------------
          1000000

[email protected]>select /*+ cluster_factor_expmple_2 */ object_id,object_name from t1 where object_id between 103 and 108;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
       103 MIGRATE$
       104 DEPENDENCY$
       105 ACCESS$
       106 I_DEPENDENCY1
       107 I_DEPENDENCY2
       108 I_ACCESS1

6 rows selected.

[email protected]>select * from table(dbms_xplan.display_cursor(null,null,‘all‘));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  1ucqsj4j0j432, child number 0
-------------------------------------
select /*+ cluster_factor_expmple_2 */ object_id,object_name from t1
where object_id between 103 and 108

Plan hash value: 50753647

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |       |       |   105 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1     |     6 |   474 |   105   (0)| 00:00:02 |
|*  2 |   INDEX RANGE SCAN          | IDX_T1 |     6 |       |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
......省略部分输出

从执行计划中可以看出,成本已经由3变为105(即增加了102),这说明我们对索引IDX_T1的聚簇因子的调整生效了。

要使成本值大于287,只需要把聚簇因子的值调整到400万。

[email protected]>exec dbms_stats.set_index_stats(ownname=>‘ZX‘,indname=>‘IDX_T1‘,clstfct=>4000000,no_invalidate=>false);

PL/SQL procedure successfully completed.

[email protected]>select clustering_factor from dba_indexes where index_name=‘IDX_T1‘;

CLUSTERING_FACTOR
-----------------
          4000000

[email protected]>select /*+ cluster_factor_expmple_3 */ object_id,object_name from t1 where object_id between 103 and 108;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
       103 MIGRATE$
       104 DEPENDENCY$
       105 ACCESS$
       106 I_DEPENDENCY1
       107 I_DEPENDENCY2
       108 I_ACCESS1

6 rows selected.

[email protected]>select * from table(dbms_xplan.display_cursor(null,null,‘all‘));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  cwkc8q61bypa6, child number 0
-------------------------------------
select /*+ cluster_factor_expmple_3 */ object_id,object_name from t1
where object_id between 103 and 108

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |   287 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |     6 |   474 |   287   (1)| 00:00:04 |
--------------------------------------------------------------------------

从上面显示的内容可以看出执行计划从索引范围扫描变成了全表扫描。

参考《基于Oracle的SQL优化》

时间: 2024-10-18 12:04:04

Oracle索引聚簇因子的含义及重要性的相关文章

oracle 索引聚簇表的工作原理

作者:Richard-Lui 一:首先介绍一下索引聚簇表的工作原理:(先创建簇,再在簇里创建索引,创建表时指定列的簇类型) 聚簇是指:如果一组表有一些共同的列,则将这样一组表存储在相同的数据库块中:聚簇还表示把相关的数据存储在同一个块上.利用聚簇,一个块可能包含多个表的数据.概念上就是如果两个或多个表经常做链接操作,那么可以把需要的数据预先存储在一起.聚簇还可以用于单个表,可以按某个列将数据分组存储. 更加简单的说,比如说,EMP表和DEPT表,这两个表存储在不同的segment中,甚至有可能存

索引聚簇因子相关

create or replace function clustering_factor(p_owner in varchar2,p_table_name in varchar2,p_column_name in varchar2)return number isl_cursor sys_refcursor;l_clustering_factor binary_integer:=0;l_block_nr binary_integer:=0;l_previous_block_nr binary_i

堆组织表,索引组织表和索引聚簇表

--- 堆组织表就不说了,其索引中记录了记录所在位置的rowid,查找的时候先找索引,然后再根据索引rowid找到块中的行数据 索引组织表,其行数据以索引形式存放,因此找到索引,就等于找到了行数据. -- 堆组织表的数据是散放的,索引和表的数据是分离的 索引组织表的索引和数据是在一起的 -- 堆组织表的存储速度因为不用考虑排序, 所以存储速度会比较快. 但是要查找符合某个条件的记录, 就必须得读取全部的记录以便筛选.而这个时候为了加快查询速度, 索引就出现了, 索引是针对少量特定字段的值拿出来进

[转帖]堆组织表,索引组织表和索引聚簇表

https://www.cnblogs.com/youngerger/p/8446399.html --- 堆组织表就不说了,其索引中记录了记录所在位置的rowid,查找的时候先找索引,然后再根据索引rowid找到块中的行数据 索引组织表,其行数据以索引形式存放,因此找到索引,就等于找到了行数据. -- 堆组织表的数据是散放的,索引和表的数据是分离的 索引组织表的索引和数据是在一起的 -- 堆组织表的存储速度因为不用考虑排序, 所以存储速度会比较快. 但是要查找符合某个条件的记录, 就必须得读取

Oracle索引总结(五)- Oracle索引种类之表簇索引(cluster index)

表簇索引(cluster index) 对于表簇索引而言,必须使用表簇. 由于簇索引与索引表簇关联紧密,无法单独拿出来总结,因此一并进行总结. 1.1 表簇的定义 表簇是一组通过相同公共列(簇键),构成的表的集合. 如上图,右侧独立的两张表,employees员工表与departments部门表,通过簇键department_id列,构成了左侧的一个表簇(cluster). 当构成表簇后,一个单独的数据块会包含多个表的数据行信息. 1.2 表簇的分类 对于oracle数据库,主要支持两种表簇:索

索引 的聚簇因子对能否用到该索引的影响的实验

在理论学习中,我们了解到,索引的聚簇因子(clustering_factor)对CBO是否选择使用索引有很大的影响.所以,首先通过以下模拟实验来加深印象: 创建测试表t0403a,共两列(ID列和COL1列),其中ID列为一个1000以内的随机数.然后在ID列上创建索引.这样做的目的就是想让该索引的聚簇因子较大.因为用这种方式创建的表中数据存放顺序与ID的大小是完全不相关的,即是混乱的,不是有序的. SQL> create table t0403a as select ceil(dbms_ran

oracle聚簇表的理解 (转自:https://blog.csdn.net/gumengkai/article/details/51009345 )

Oracle支持两种类型的聚簇:索引聚簇和哈希聚簇 一.索引聚簇表的原理 聚簇:如果一些表有一些共同的列,则将这样一组表存储在相同的数据块中 聚簇还表示把相关的数据存储在同一个块上.利用聚簇,一个块可能包含多个表的数据. 概念上就是说如果两个表或多个表经常做连接操作,就可以预先把需要的数据也存储在一起. 聚簇还可以用于单个表,可以按某个列将数据分组存储. 简单的说,簇就是一组表,由一组共享相同数据块的多个表组成,将经常一起使用的表组合在一起成簇 就可以提高处理效率. 二.使用方法 建立的顺序:簇

谈一下oracle索引

索引的应用:索引的分类:逻辑分类:单列索引 & 复合列索引create index i_emp_ename on emp (ename);create index i_emp_3 on emp (empno,ename,sal);非唯一键索引 & 唯一键索引create unique index i_emp_ename on emp (ename);基于函数的索引create index i_emp_ename on emp (lower(ename));应用程序域索引: oracle查看

[转]Oracle 索引质量分析

http://blog.csdn.net/leshami/article/details/23687137 索引质量的高低对数据库整体性能有着直接的影响.良好高质量的索引使得数据库性能得以数量级别的提升,而低效冗余的索引则使得数据库性能缓慢如牛,即便是使用高档的硬件配置.因此对于索引在设计之初需要经过反复的测试与考量.那对于已经置于生产环境中的数据库,我们也可以通过查询相关数据字典得到索引的质量的高低,通过这个分析来指导如何改善索引的性能.下面给出了演示以及索引创建的基本指导原则,最后给出了索引