聚簇因子和执行计划的联系

在平时的工作中,可能会碰到一种很奇怪的问题,本来在生产环境中有些sql语句执行没有问题,一个很普通的查询预期走了索引扫面,但是拷贝数据到其它环境之后,就发现却走了全表扫描。
或者情况相反,本来出现问题的查询走了全表扫描,我们尝试在测试环境中浮现,但是测试环境中在相同的数据量的情况下,查询却又走了索引扫描,问题无法复现了。
出现这种情况的原因比较复杂,涉及很多的原因,其中一个很重要的原因就是聚簇因子的导致的。
聚簇因子是一个与索引相关的统计信息,它通过查看表中的数据块来进行计算得到。
对于这个问题,可能直接说理论会有些枯燥。可以通过如下的问题来进行说明。

create table t1 as select trunc(rownum/100) id ,object_name from all_objects where rownum
create table t2 as select mod(rownum,100) id ,object_name from all_objects where rownum

create index inx_t1 on t1(id);
create index inx_t2 on t2(id);

exec dbms_stats.gather_table_stats(null,'T1',CASCADE=>true);
exec dbms_stats.gather_table_stats(null,'T2',CASCADE=>true);

查看表t1的数据类似下面的格式。
SQL> select *from t1 where rownum

        ID OBJECT_NAME
---------- ------------------------------
         0 ICOL$
         0 I_USER1
         0 CON$
         0 UNDO$
         0 C_COBJ#
         0 I_OBJ#
         0 PROXY_ROLE_DATA$
         0 I_IND1
         0 I_CDEF2
         0 I_OBJ5
         0 I_PROXY_ROLE_DATA$_1
         0 FILE$
         0 UET$
         0 I_FILE#_BLOCK#
         0 I_FILE1
         0 I_CON1
         0 I_OBJ3
         0 I_TS#
         0 I_CDEF4

19 rows selected.

查看表t2的数据类似下面的格式。
SQL> select *from t2 where rownum

        ID OBJECT_NAME
---------- ------------------------------
         1 ICOL$
         2 I_USER1
         3 CON$
         4 UNDO$
         5 C_COBJ#
         6 I_OBJ#
         7 PROXY_ROLE_DATA$
         8 I_IND1
         9 I_CDEF2
        10 I_OBJ5
        11 I_PROXY_ROLE_DATA$_1
        12 FILE$
        13 UET$
        14 I_FILE#_BLOCK#
        15 I_FILE1
        16 I_CON1
        17 I_OBJ3
        18 I_TS#
        19 I_CDEF4

19 rows selected.
下面的表格能够简要的说明数据的分布。
T1中数据的分布。

0 0 0 0 0
0 0 0 0 0
. . . . .
1 1 1 1 1
1 1 1 1 1
. . . . .

T2中数据的分布。

1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
. . . . .
1 2 3 4 5
6 7 8 9 10

我们来看看同样的查询对应的执行计划。

SQL>select *from t1 where id=2;
Execution Plan
----------------------------------------------------------
Plan hash value: 2808986199

--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |   100 |  1800 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1     |   100 |  1800 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | INX_T1 |   100 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ID"=2)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         17  consistent gets
          0  physical reads
          0  redo size
       4130  bytes sent via SQL*Net to client
        586  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

SQL>select *from t1 where id=2;

10 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 1513984157

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    10 |   180 |     4   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   |    10 |   180 |     4   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ID"=2)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        820  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         10  rows processed
可以看到一个走了索引扫描,一个走了全表扫描。这个时候我们再来看看聚簇因子。
SQL>select i.table_name,i.index_name,i.CLUSTERING_FACTOR,t.blocks,i.NUM_ROWS from user_tables t,user_indexes i where t.table_name=i.table_name and  t.table_name in ('T1','T2');
TAB INDEX_NAME           CLUSTERING_FACTOR     BLOCKS   NUM_ROWS
--- -------------------- ----------------- ---------- ----------
T1  INX_T1                               4          7        999
T2  INX_T2                             365          7        999

表t2的数据分布比较散,表的聚簇度高,接近于表中的数据,对于id=2,因为数据分布得很开,扫描的数据块就很很多,就很可能走全表扫描。而表中t1的数据聚簇度低,比如要查找id=2的数据,因为这些数据分布比较集中,扫描的数据块就要很少,索引就很可能走索引扫描。

对于聚簇因子,可以通过重建索引,重建表,或者重新组织索引来改进,但是从实现的角度来说很困难,毕竟数据的分布情况很难模拟,如果要进行问题的复现和排查还是需要掌握不少的细节,通过备份库来复现问题也是一种思路。
tom对于聚簇因子的解释如下。
https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:1032431852141

Note that typically only 1 index per table will be heavily clustered (if any).  It would
be extremely unlikely for 2 indexes to be very clustered.

If you want an index to be very clustered -- consider using index organized tables.  They
force the rows into a specific physical location based on their index entry.

Otherwise, a rebuild of the table is the only way to get it clustered (but you really
don't want to get into that habit for what will typically be of marginal overall
improvement).

时间: 2024-09-18 07:02:03

聚簇因子和执行计划的联系的相关文章

一个执行计划异常变更的案例 - 外传之聚簇因子(Clustering Factor)

之前的几篇文章: <一个执行计划异常变更的案例 - 前传> <一个执行计划异常变更的案例 - 外传之绑定变量窥探> <一个执行计划异常变更的案例 - 外传之查看绑定变量值的几种方法> <一个执行计划异常变更的案例 - 外传之rolling invalidation> 这个案例中涉及到了聚簇因子,所以本篇文章是这个系列的又一篇外传,写过上面几篇后,感觉现在就像打怪,见着真正的大BOSS之前,要经历各种小怪的骚扰,之所以写着几篇文章,真是因为这个案例涉及了很多知

Oracle聚簇因子(Clustering factor,CF)

Oracle 聚簇因子(Clustering factor)   聚簇因子是 Oracle 统计信息中在CBO优化器模式下用于计算cost的参数之一,决定了当前的SQL语句是否走索引,还是全表扫描以及是否作为嵌套连接外部表等.如此这般,那到底什么是聚簇因子,那些情况下会影响到聚簇因子,以及如何提高聚簇因子?本文将对此展开描述.   1.堆表的存储方式    Oralce 数据库系统中最普通,最为常用的即为堆表.     堆表的数据存储方式为无序存储,也就是任意的DML操作都可能使得当前数据块存在

Oracle聚簇因子

1.聚簇因子(clustering_factor):是使用B树索引进行区间扫描的成本很重要因素,反映数据在表中分布的随机程度 2.聚簇因子的计算方法: ①扫描索引 ②比较某行的ROWID和前一行的ROWID,如果这两个ROWID不属于同一个数据块,那么聚簇因子增加1  --对选择最优查询路径影响最大的只有列的选择率和聚簇因子 3.实验测试 create table t1 as select trunc((rownum-1)/100) id, rpad(rownum,100) t_pad from

如何执行计划的生成过程

生成执行计划的过程:编译器:分为三部分: 2.优化器:--包括三个部分 查询转换器RBO-->CBO,目前是CBO ,optimizer_mode--ALL_ROWS参数值,适合OLTP.FIRST_ROWS_N适合分页,OLAP. 查询转化器: 视图合并--视图时直接用视图SQL语句对应表做基表进行连接. 谓词推进,子查询非嵌套化--相关子查询:OR--UNION合并 成本估算器: 拿数据字典里统计信息,主要有: 表:dba_tab_statistics --行数,块数 表中字段:dba_ta

一个执行计划异常变更引发的Oracle性能诊断优化

最近有一个OLTP应用使用的Oracle数据库突然出现性能问题,DBA发现有一些delete语句执行时间骤长,消耗大量系统资源,导致应用响应时间变长积Q.   辅助信息: 应用已经很久未做过更新上线了. 据开发人员反馈,从之前的应用日志看,未出现处理时间逐步变长的现象. 这是一套RAC+DG的环境,11g的版本. 这次突然出现大量执行时间超长的SQL语句,是一条删除语句,delete from table where key1=:1 and key2=:2 and ...(省略此案例不会用到的其

oracle 12c R1执行计划新特性-table access by index rowid batched和INMOMEORY OPTION

oracle 12c R1执行计划在索引回表阶段oracle推出了batched特性,类似于oracle 11g中在nested loop中被驱动表回表时的向量IO,也是为了有效的解决表中数据无序性(索引的聚簇因子),下面看实际测试用例: 数据库版本:12.1.0.2版本 sys@CRMDB2> explain plan for SELECT offering_inst_id,        offering_id,        owner_party_role_type,        ow

Oracle 历史SQL语句执行计划的对比与分析

    基于CBO优化器的环境中,SQL执行计划的生成依赖于统计信息的真实与完整.如列的离散度,列上的直方图,索引的可用性,索引上的聚簇因子.当这些信息是真实完整的情况下,CBO优化器通常都可以制定最优的执行计划.也正因此CBO优化器也灵活,难以控制,任一信息的不真实或缺失都可能导致执行计划发生变化而产生多个版本.经常碰到的情形是之前的某个SQL语句前阵子还不是TOP SQL,而最近变成了TOP SQL.或者说之前尽管是TOP SQL但,但最近尽然成了TOP 1.对于此情形,我们可以比对SQL语

Oracle 全表扫描及其执行计划(full table scan)

    全表扫描是Oracle访问数据库表是较为常见的访问方式之一.很多朋友一看到SQL语句执行计划中的全表扫描,就要考虑对其进行修理一番.全表扫描的存在,的确存在可能优化的余地.但事实上很多时候全表扫描也并非是最低效的,完全要看不同的情形与场合,任一方式都是有利有弊的,也就是具体情况要具体分析.本文描述了什么是全表扫描以及何时发生全表扫描,何时全表扫描才低效.  本文涉及到的相关链接:     高水位线和全表扫描      启用 AUTOTRACE 功能     Oracle 测试常用表BIG

一个执行计划异常变更的案例 - 外传之查询执行计划的几种方法

之前的几篇文章: <一个执行计划异常变更的案例 - 前传> <一个执行计划异常变更的案例 - 外传之绑定变量窥探> <一个执行计划异常变更的案例 - 外传之查看绑定变量值的几种方法> <一个执行计划异常变更的案例 - 外传之rolling invalidation> <一个执行计划异常变更的案例 - 外传之聚簇因子(Clustering Factor)> 本篇外传主要介绍一些常用的执行计划查看方法. SQL的执行计划实际代表了目标SQL在Orac