索引聚簇因子用于测量相对于某个索引值(如雇员姓氏)的行顺序。被索引值的行存储得越有序,则聚簇因子越低。因为我们常用的表是堆表,数据的存储是“无序”存放在磁盘或存储上;如果所查的数据越无序越分散,查询的逻辑IO
虽然一样,但是物理IO的代价就可能很高了。
如果聚簇因子较高,则在大型索引范围扫描过程中,数据库将执行相对较高数目的I/O。索引条目指向随机表块,因此数据库可能必须一遍又一遍地来回重读索引所指向的同一数据块。
如果聚簇因子较低,则在大型索引范围扫描过程中数据库将执行相对较低数目的I/O。在一个范围内的索引键倾向于指向相同的数据块,因此该数据库不必来回重读相同的数据块。
例如:
场景:以姓氏为顺序存放多条数据,并放置多个数据块上,
假设在姓氏列上存在一个索引。每个姓氏条目对应于一个 rowid。从概念上讲,索引条目看起来如下所示:
Abel,block1row1
Ande,block1row2
Atkinson,block1row3
Austin,block1row4
Baer,block1row5
假设在雇员 ID 列上存在另一个单独的索引。从概念上讲,索引条目可能看起来像下面这样,雇员 id几乎分布在这整个两个数据块的任意位置:
100,block1row50
101,block2row1
102,block1row9
103,block2row19
104,block2row39
105,block1row4
.
.
通过ALL_INDEXES 查看这两个索引的聚簇因子。EMP_NAME_IX 的聚簇因子较低,这意味着在一个单一叶块中的相邻索引条目倾向于指向同一个数据块中的行。
EMP_EMP_ID_PK 的聚簇因子较高,这意味着在相同的叶块中的相邻索引条目不太可能指向同一个数据块中的行。
数据库版本:
SQL> select * from v$version ;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
索引块无序存储情况下:
SQL> create table t_f as select * from dba_objects where 0=1;
Table created.
SQL> begin
2 for i in 1..10 loop
3 insert into t_f select * from dba_objects order by i;
4 commit;
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> set wrap off
SQL> col owner for a10
SQL> col segment_name for a15
SQL> select owner, segment_name, blocks, extents,bytes/1024/1024||'M' "size" from dba_segments where owner='SCOTT' and segment_name='T_F';
OWNER SEGMENT_NAME BLOCKS EXTENTS size
---------- --------------- ---------- ---------- -------------------------------
SCOTT T_F 11136 82 87M
SQL> select count(1) from t_f ;
COUNT(1)
----------
753430
SQL> col owner for a10;
SQL> col index_name for a10;
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F_ID 753430 753430 ====>从这里可以看出聚簇因子与行数是一样的,通过统计信息的收集也是一样,如下,说明每次读一条记录都会产生一个物理IO
SQL> exec dbms_stats.gather_table_stats('SCOTT','T_F',cascade => true);
PL/SQL procedure successfully completed.
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F_ID 753430 753430
SQL> select blocks from dba_tables where table_name='T_F';
BLOCKS
----------
10989
索引块有序存储情况下:
SQL> create table t_f2 as select * from dba_objects where 0=1;
Table created.
SQL> insert into t_f2 select * from t_f order by object_id;
753430 rows created.
SQL> commit;
Commit complete.
SQL> select count(1) from t_f2;
COUNT(1)
----------
753430
SQL> create index idx_t_f2_id on t_f2(object_id);
Index created.
SQL> select owner, segment_name, segment_type,blocks, extents,bytes/1024/1024||'M' "SIZE" from dba_segments where owner='SCOTT' and segment_name=upper('idx_t_f2_id');
truncating (as requested) before column EXTENTS
OWNER SEGMENT_NAME SEGMENT_TYPE BLOCKS SIZE
---------- --------------- ------------------------------------ ---------- -----
SCOTT IDX_T_F2_ID INDEX 1792 14M
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f2_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F2_ID 12155 753430===>从这里可以看出聚簇因子与block数相似,通过统计信息的收集也是一样
SQL> exec dbms_stats.gather_table_stats('SCOTT','T_F2',cascade => true);
PL/SQL procedure successfully completed.
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f2_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F2_ID 12155 753430
SQL> select blocks from dba_tables where table_name='T_F2';
BLOCKS
----------
11117
一些简单总结:
1)、整个索引扫描完毕后,就得到了该索引的cluster factor;
2)、如果ClusteringFactor接近于表存储的块数,说明这张表是按照索引字段的顺序存储的,最佳状态就是两个数值相等,但是在生产库上,很难发现活跃的表时这种理想状态;
3)、 如果ClusteringFactor接近于行的数量,那说明这张表不是按索引字段顺序存储的,这样就可能导致多次去访问同一个块,导致产生多次物理IO;
4)、影响这个因素的最佳操作方法就是重建表,然后重新顺序导入数据,不过一般很难做到
5)、这个参数能帮助我们了解,有时候,是否走索引,并非就是数据量占据100%的因素,我很少能影响这个这个因素
虽然一样,但是物理IO的代价就可能很高了。
如果聚簇因子较高,则在大型索引范围扫描过程中,数据库将执行相对较高数目的I/O。索引条目指向随机表块,因此数据库可能必须一遍又一遍地来回重读索引所指向的同一数据块。
如果聚簇因子较低,则在大型索引范围扫描过程中数据库将执行相对较低数目的I/O。在一个范围内的索引键倾向于指向相同的数据块,因此该数据库不必来回重读相同的数据块。
例如:
场景:以姓氏为顺序存放多条数据,并放置多个数据块上,
假设在姓氏列上存在一个索引。每个姓氏条目对应于一个 rowid。从概念上讲,索引条目看起来如下所示:
Abel,block1row1
Ande,block1row2
Atkinson,block1row3
Austin,block1row4
Baer,block1row5
假设在雇员 ID 列上存在另一个单独的索引。从概念上讲,索引条目可能看起来像下面这样,雇员 id几乎分布在这整个两个数据块的任意位置:
100,block1row50
101,block2row1
102,block1row9
103,block2row19
104,block2row39
105,block1row4
.
.
通过ALL_INDEXES 查看这两个索引的聚簇因子。EMP_NAME_IX 的聚簇因子较低,这意味着在一个单一叶块中的相邻索引条目倾向于指向同一个数据块中的行。
EMP_EMP_ID_PK 的聚簇因子较高,这意味着在相同的叶块中的相邻索引条目不太可能指向同一个数据块中的行。
数据库版本:
SQL> select * from v$version ;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
索引块无序存储情况下:
SQL> create table t_f as select * from dba_objects where 0=1;
Table created.
SQL> begin
2 for i in 1..10 loop
3 insert into t_f select * from dba_objects order by i;
4 commit;
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> set wrap off
SQL> col owner for a10
SQL> col segment_name for a15
SQL> select owner, segment_name, blocks, extents,bytes/1024/1024||'M' "size" from dba_segments where owner='SCOTT' and segment_name='T_F';
OWNER SEGMENT_NAME BLOCKS EXTENTS size
---------- --------------- ---------- ---------- -------------------------------
SCOTT T_F 11136 82 87M
SQL> select count(1) from t_f ;
COUNT(1)
----------
753430
SQL> col owner for a10;
SQL> col index_name for a10;
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F_ID 753430 753430 ====>从这里可以看出聚簇因子与行数是一样的,通过统计信息的收集也是一样,如下,说明每次读一条记录都会产生一个物理IO
SQL> exec dbms_stats.gather_table_stats('SCOTT','T_F',cascade => true);
PL/SQL procedure successfully completed.
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F_ID 753430 753430
SQL> select blocks from dba_tables where table_name='T_F';
BLOCKS
----------
10989
索引块有序存储情况下:
SQL> create table t_f2 as select * from dba_objects where 0=1;
Table created.
SQL> insert into t_f2 select * from t_f order by object_id;
753430 rows created.
SQL> commit;
Commit complete.
SQL> select count(1) from t_f2;
COUNT(1)
----------
753430
SQL> create index idx_t_f2_id on t_f2(object_id);
Index created.
SQL> select owner, segment_name, segment_type,blocks, extents,bytes/1024/1024||'M' "SIZE" from dba_segments where owner='SCOTT' and segment_name=upper('idx_t_f2_id');
truncating (as requested) before column EXTENTS
OWNER SEGMENT_NAME SEGMENT_TYPE BLOCKS SIZE
---------- --------------- ------------------------------------ ---------- -----
SCOTT IDX_T_F2_ID INDEX 1792 14M
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f2_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F2_ID 12155 753430===>从这里可以看出聚簇因子与block数相似,通过统计信息的收集也是一样
SQL> exec dbms_stats.gather_table_stats('SCOTT','T_F2',cascade => true);
PL/SQL procedure successfully completed.
SQL> select owner,index_name, clustering_factor, num_rows from dba_indexes where owner='SCOTT' and index_name=upper('idx_t_f2_id');
OWNER INDEX_NAME CLUSTERING_FACTOR NUM_ROWS
---------- ---------- ----------------- ----------
SCOTT IDX_T_F2_ID 12155 753430
SQL> select blocks from dba_tables where table_name='T_F2';
BLOCKS
----------
11117
一些简单总结:
1)、整个索引扫描完毕后,就得到了该索引的cluster factor;
2)、如果ClusteringFactor接近于表存储的块数,说明这张表是按照索引字段的顺序存储的,最佳状态就是两个数值相等,但是在生产库上,很难发现活跃的表时这种理想状态;
3)、 如果ClusteringFactor接近于行的数量,那说明这张表不是按索引字段顺序存储的,这样就可能导致多次去访问同一个块,导致产生多次物理IO;
4)、影响这个因素的最佳操作方法就是重建表,然后重新顺序导入数据,不过一般很难做到
5)、这个参数能帮助我们了解,有时候,是否走索引,并非就是数据量占据100%的因素,我很少能影响这个这个因素