聚簇因子是 Oracle 统计信息中在CBO优化器模式下用于计算cost的参数之一,决定了当前的SQL语句是否走索引,还是全表扫描以及是否作为嵌套连接外部表等。如此这般,那到底什么是聚簇因子,那些情况下会影响到聚簇因子,以及如何提高聚簇因子?本文将对此展开描述。
1、堆表的存储方式
Oralce 数据库系统中最普通,最为常用的即为堆表。
堆表的数据存储方式为无序存储,也就是任意的DML操作都可能使得当前数据块存在可用的空闲空间。
处于节省空间的考虑,块上的可用空闲空间会被新插入的行填充,而不是按顺序填充到最后被使用的块上。
上述的操作方式导致了数据的无序性的产生。
当创建索引时,会根据指定的列按顺序来填充到索引块,缺省的情况下为升序。
新建或重建索引时,索引列上的顺序是有序的,而表上的顺序是无序的,也就是存在了差异,即表现为聚簇因子。
2、什么是聚簇因子(clustering factor/CF)
聚簇因子是基于表上索引列上的一个值,每一个索引都有一个聚簇因子。
用于描述索引块上与表块上存储数据在顺序上的相似程度,也就说表上的数据行的存储顺序与索引列上顺序是否一致。
在全索引扫描中,CF的值基本上等同于物理I/O或块访问数,如果相同的块被连续读,则Oracle认为只需要1次物理I/O。
好的CF值接近于表上的块数,而差的CF值则接近于表上的行数。
聚簇因子在索引创建时就会通过表上存存在的行以及索引块计算获得。
3、Oracle 如何计算聚簇因子
执行或预估一次全索引扫描。
检查索引块上每一个rowid的值,查看是否前一个rowid的值与后一个指向了相同的数据块,如果指向了不相同的数据块则CF的值增加1。
当索引块上的每一个rowid被检查完毕,即得到最终的CF值。
4、聚簇因子图示
a、良好的索引与聚簇因子的情形
b、良好的索引、差的聚簇因子的情形
c、差的索引、差的聚簇因子的情形
5、影响聚簇因子的情形
当插入到表的数据与索引的顺序相同时,可以提高聚簇因子(接近表上的块数)。
因此,任意影响该顺序的情形都将导致索引列上的聚簇因子变差。
如列的顺序,反向索引,空闲列表或空闲列表组。
6、提高聚簇因子
堆表的数据存储是无序存储,因此需要使无序变为有序。下面是提高聚簇因子的办法。
a、对于表上的多个索引以及组合索引的情形,索引的创建应考虑按应该按照经常频繁读取的大范围数据的读取顺序来创建索引。
b、定期重构表(针对堆表),也就是使得表与索引上的数据顺序更接近。注意,是重构表,而不是重建索引。
重建索引并不能显剧提高CF的值,因为索引列通常是有序的,无序的是原始表上的数据。
提取原始表上的数据到一个临时表,禁用依赖于该表的相关约束,truncate原始表,再将临时表的数据按索引访问顺序填充到原始表。
c、使用聚簇表来代替堆表。
7、实战聚簇因子随索引结构变化的情形
-
a、演示环境
-
scott@SYBO2SZ> select * from v$version where rownum<2;
-
-
BANNER
-
-
Oracle Database 10g Release 10.2.0.3.0 - 64bit Production
-
-
b、列顺序对CF的影响
-
-
scott@SYBO2SZ> create table t as select * from dba_objects order by object_name;
-
-
scott@SYBO2SZ> create index i_obj_name on t(object_name);
-
-
scott@SYBO2SZ> create index i_obj_id on t(object_id);
-
-
cott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','T',cascade=>true);
-
-
PL/SQL procedure successfully completed.
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: T
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
1 I_OBJ_NAME 241 29476 1 1 675 20130418 17:00:42 695 48931
-
1 I_OBJ_ID 108 48931 1 1 24887 20130418 17:06:10 695 48931
-
-
-
-
-
-
c、组合索引对CF的影响
-
-
-
-
-
scott@SYBO2SZ> create index i_obj_name_id on t (object_name, object_id);
-
-
scott@SYBO2SZ> create index i_obj_id_name on t (object_id, object_name);
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','T',cascade=>true)
-
-
PL/SQL procedure successfully completed.
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: T
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
1 I_OBJ_NAME 241 29476 1 1 675 20130418 17:17:17 695 48931
-
1 I_OBJ_ID 108 48931 1 1 24887 20130418 17:17:17 695 48931
-
1 I_OBJ_NAME_ID 274 48931 1 1 945 20130418 17:17:17 695 48931
-
1 I_OBJ_ID_NAME 274 48931 1 1 24887 20130418 17:17:18 695 48931
-
-
-
-
-
-
-
d、反向索引对CF的影响
-
-
-
scott@SYBO2SZ> create table t2 nologging as select * from t;
-
-
scott@SYBO2SZ> create index i_obj_name_reverse on t2(object_name) reverse;
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','T2',cascade=>true)
-
-
PL/SQL procedure successfully completed.
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: T2
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
1 I_OBJ_NAME_REVERSE 241 29476 1 1 28104 20130418 17:22:49 695 48931
-
-
-
-
8、实战聚簇因子随DML变化的情形
-
a、创建演示环境
-
scott@SYBO2SZ> @cr_big_tb 1000000
-
-
Table created.
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: BIG_TABLE
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_PK 2088 1000000 1 1 14489 20130422 12:27:43 14652 999712
-
-
b、模拟DML操作
-
-
scott@SYBO2SZ> create table big_table_tmp nologging as select * from big_table where id>=10000 and id<=200000;
-
-
scott@SYBO2SZ> delete from big_table nologging where id>=10000 and id<=200000;
-
-
scott@SYBO2SZ> commit;
-
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: BIG_TABLE
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_PK 2088 1000000 1 1 14489 20130422 12:27:43 14652 999712
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','BIG_TABLE',cascade=>true);
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: BIG_TABLE
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_PK 1692 809999 1 1 11732 20130422 12:31:45 14652 808497
-
-
-
scott@SYBO2SZ> insert into big_table nologging select * from big_table_tmp where id>=150000 and id<=200000
-
2 order by object_name;
-
-
scott@SYBO2SZ> insert into big_table nologging select * from big_table_tmp where id>=10000 and id<150000
-
2 order by object_name;
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','BIG_TABLE',cascade=>true);
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: BIG_TABLE
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_PK 2088 1000000 1 1 114256 20130422 12:33:31 14652 998513
-
-
-
scott@SYBO2SZ> alter table big_table move;
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: BIG_TABLE
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_PK 2088 1000000 1 1 114256 20130422 12:33:31 14652 998513
-
-
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','BIG_TABLE',cascade=>true);
-
BEGIN dbms_stats.gather_table_stats('SCOTT','BIG_TABLE',cascade=>true); END;
-
-
*
-
ERROR at line 1:
-
ORA-20000: index "SCOTT"."BIG_TABLE_PK" or partition of such index is in unusable state
-
ORA-06512: at "SYS.DBMS_STATS", line 13182
-
ORA-06512: at "SYS.DBMS_STATS", line 13202
-
ORA-06512: at line 1
-
-
scott@SYBO2SZ> alter index big_table_pk rebuild nologging;
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','BIG_TABLE',cascade=>true);
-
-
PL/SQL procedure successfully completed.
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: BIG_TABLE
-
Enter value for owner: SCOTT
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_PK 2088 1000000 1 1 118384 20130422 12:36:31 14649 999427
-
-
c、重建big_table
-
-
scott@SYBO2SZ> drop table big_table_tmp purge;
-
-
scott@SYBO2SZ> create table big_table_tmp nologging as select * from big_table order by id;
-
-
scott@SYBO2SZ> create unique index big_table_tmp_pk on big_table_tmp(id);
-
-
scott@SYBO2SZ> alter table big_table_tmp add constraint big_table_tmp_pk primary key(id) using index big_table_tmp_pk;
-
-
scott@SYBO2SZ> exec dbms_stats.gather_table_stats('SCOTT','BIG_TABLE_TMP',cascade=>true);
-
-
scott@SYBO2SZ> @idx_stat
-
Enter value for input_table_name: big_table_tmp
-
Enter value for owner: scott
-
-
-
-
-
AVG LEAF BLKS AVG DATA BLKS
-
BLEV IDX_NAME LEAF_BLKS DST_KEYS PER KEY PER KEY CLUST_FACT LAST_ANALYZED TB_BLKS TB_ROWS
-
-
2 BIG_TABLE_TMP_PK 2088 1000000 1 1 14486 20130422 12:38:37 14649 995891
-
-
d、比较不同的CF对查询性能的影响
-
-
scott@SYBO2SZ> set autot trace;
-
scott@SYBO2SZ> select * from big_table where id between 10000 and 15000;
-
-
5001 rows selected.
-
-
Execution Plan
-
-
Plan hash value: 3747652938
-
-
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-
| 0 | SELECT STATEMENT | | 5001 | 478K| 606 (0)| 00:00:08 |
-
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_TABLE | 5001 | 478K| 606 (0)| 00:00:08 |
-
|* 2 | INDEX RANGE SCAN | BIG_TABLE_PK | 5001 | | 13 (0)| 00:00:01 |
-
-
-
Predicate Information (identified by operation id):
-
-
-
2 - access("ID">=10000 AND "ID"<=15000)
-
-
Statistics
-
-
1 recursive calls
-
0 db block gets
-
2993 consistent gets
-
531 physical reads
-
116 redo size
-
287976 bytes sent via SQL*Net to client
-
4155 bytes received via SQL*Net from client
-
335 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
5001 rows processed
-
-
-
-
scott@SYBO2SZ> select * from big_table_tmp where id between 10000 and 15000;
-
-
5001 rows selected.
-
-
Execution Plan
-
-
Plan hash value: 1127920103
-
-
-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-
-
| 0 | SELECT STATEMENT | | 4982 | 476K| 86 (0)| 00:00:02 |
-
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_TABLE_TMP | 4982 | 476K| 86 (0)| 00:00:02 |
-
|* 2 | INDEX RANGE SCAN | BIG_TABLE_TMP_PK | 4982 | | 13 (0)| 00:00:01 |
-
-
-
Predicate Information (identified by operation id):
-
-
-
2 - access("ID">=10000 AND "ID"<=15000)
-
-
Statistics
-
-
1 recursive calls
-
0 db block gets
-
750 consistent gets
-
76 physical reads
-
0 redo size
-
287976 bytes sent via SQL*Net to client
-
4155 bytes received via SQL*Net from client
-
335 SQL*Net roundtrips to/from client
-
0 sorts (memory)
-
0 sorts (disk)
-
5001 rows processed
-
-
-
-
-
9、小结
a、任意情形下(堆表),表上数据的存储只能按照一种特定的顺序进行存储。
b、由上面的特性决定了表上的只有一个特定的索引列(单索引或组合索引)具有最佳的CF值。
c、索引的创建应考虑按应该按照经常频繁读取的大范围数据的读取顺序来创建索引,以保证得到最佳的CF值。
d、索引在被创建之时,基于该索引列上的CF值即被产生,但表上的DML操作后需要收集统计信息才可以更新CF的值。
e、基于表上频繁的DML操作,尤其是delete后再新增记录,可用空闲空间被填充,将使得CF的值呈增大趋势。
f、alter table move tabname并不会影响CF的值,该功能只是移动高水位线,且不释放空间。
g、重建索引对CF的值收效甚微,因为原始表数据存储顺序未发生根本变化。
h、CF的值是影响查询分析器对执行计划的评估与生成的因素之一(即是否走索引还是全表扫描,嵌套连接时哪个表为驱动表等)。
i、通过重建表或使用聚簇表来改进CF的值,建议将原始表数据填充到临时表,禁用依赖于该表的所有约束后truncate该表,再从临时表导回数据(按顺序),启用约束。
j、不推荐使用create table as select(CTAS),因为表上的一些特性会被忽略,可以用SQL*Plus copy方式来实现。具体参考: 当心 CREATE TABLE AS
更多参考
有关Oracle RAC请参考
使用crs_setperm修改RAC资源的所有者及权限
使用crs_profile管理RAC资源配置文件
RAC 数据库的启动与关闭
再说 Oracle RAC services
Services in Oracle Database 10g
Migrate datbase from single instance to Oracle RAC
Oracle RAC 连接到指定实例
Oracle RAC 负载均衡测试(结合服务器端与客户端)
Oracle RAC 服务器端连接负载均衡(Load Balance)
Oracle RAC 客户端连接负载均衡(Load Balance)
ORACLE RAC 下非缺省端口监听配置(listener.ora tnsnames.ora)
ORACLE RAC 监听配置 (listener.ora tnsnames.ora)
配置 RAC 负载均衡与故障转移
CRS-1006 , CRS-0215 故障一例
基于Linux (RHEL 5.5) 安装Oracle 10g RAC
使用 runcluvfy 校验Oracle RAC安装环境
有关Oracle 网络配置相关基础以及概念性的问题请参考:
配置非默认端口的动态服务注册
配置sqlnet.ora限制IP访问Oracle
Oracle 监听器日志配置与管理
设置 Oracle 监听器密码(LISTENER)
配置ORACLE 客户端连接到数据库
有关基于用户管理的备份和备份恢复的概念请参考
Oracle 冷备份
Oracle 热备份
Oracle 备份恢复概念
Oracle 实例恢复
Oracle 基于用户管理恢复的处理
SYSTEM 表空间管理及备份恢复
SYSAUX表空间管理及恢复
Oracle 基于备份控制文件的恢复(unsing backup controlfile)
有关RMAN的备份恢复与管理请参考
RMAN 概述及其体系结构
RMAN 配置、监控与管理
RMAN 备份详解
RMAN 还原与恢复
RMAN catalog 的创建和使用
基于catalog 创建RMAN存储脚本
基于catalog 的RMAN 备份与恢复
RMAN 备份路径困惑
使用RMAN实现异机备份恢复(WIN平台)
使用RMAN迁移文件系统数据库到ASM
linux 下RMAN备份shell脚本
使用RMAN迁移数据库到异机
有关ORACLE体系结构请参考
Oracle 表空间与数据文件
Oracle 密码文件
Oracle 参数文件
Oracle 联机重做日志文件(ONLINE LOG FILE)
Oracle 控制文件(CONTROLFILE)
Oracle 归档日志
Oracle 回滚(ROLLBACK)和撤销(UNDO)
Oracle 数据库实例启动关闭过程
Oracle 10g SGA 的自动化管理
Oracle 实例和Oracle数据库(Oracle体系结构)