列中有索引而执行计划不走索引的原因之一的验证

许多ORACLE开发人员对于当他们只检索很少量的行时优化器选择全表扫描而感到困惑,而没有意识到优化器考虑了表中列值的集簇因子。 

下面这一段是Oracle 手册中关于CLUSTERING_FACTOR的说明:

Indicates the amount of order of the rows in the table based on the values of the index.

  • If the value is near the number of blocks, then the table is very well ordered. In this case, the index entries in a single leaf block tend to point to rows in the same data blocks.

  • If the value is near the number of rows, then the table is very randomly ordered. In this case, it is unlikely that index entries in the same leaf block point to rows in the same data blocks.

下面引用下CUUG给出的clustering_factor对索引列的影响的图,希望作者不要介意。

    

左边这张表示的集簇因子与表数据块相同,说明数据存储相对集中,查询时走索引减少IO量,提高效率。

右边的图表示集簇因子与表的行数接近了,说明数据存储非常分散,如果使用索引会先去检索一遍索引,然后会在每一个数据块再去找数据,这样而言就会增加IO的负担,还不如直接进行全表扫描,减少了检索索引的IO消耗。


下面做实验来验证一下

SQL> create table test_clu_factor as select * from emp;

表已创建。

SQL> insert into test_clu_factor select * from emp;

已创建14行。

。。。。。。一直这个操作到210行,然后突然想到还有更快的构造数据的方法。

SQL> insert into test_clu_factor select * from test_clu_facto

已创建210行。
。。。。。。。。。。一直这个操作

SQL> /

已创建13440行。

SQL> commit;

提交完成。

SQL> create index cluter_ind on test_clu_factor (ename);

索引已创建。

SQL> analyze table test_clu_factor compute statistics;

表已分析。

SQL> select ta.NUM_ROWS,ta.BLOCKS,ta.AVG_ROW_LEN,ind.clustering_factor from dba_tables ta,dba_indexes ind
  2     where ta.TABLE_NAME=ind.table_name and ind.table_name=upper('test_clu_factor');

  NUM_ROWS     BLOCKS AVG_ROW_LEN CLUSTERING_FACTOR
---------- ---------- ----------- -----------------
     26880        183          40              2226

这里的数据块是183块,而集簇因子是2226,数据相差太大,根据前面的提示说明查询应该走全部扫描才对,下面我们看看是否走全表扫描?

20:58:00 SQL> select * from test_clu_factor where ename='SCOTT';

已选择1920行。

执行计划
----------------------------------------------------------
Plan hash value: 71171066

--------------------------------------------------------------------------------
-----
| Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time
    |
--------------------------------------------------------------------------------
-----
|   0 | SELECT STATEMENT  |                 |  1920 | 61440 |    52   (2)| 00:00
:01 |
|*  1 |  TABLE ACCESS FULL| TEST_CLU_FACTOR |  1920 | 61440 |    52   (2)| 00:00
:01 |
--------------------------------------------------------------------------------
-----

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

   1 - filter("ENAME"='SCOTT')

统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        309  consistent gets
          0  physical reads
          0  redo size
      26581  bytes sent via SQL*Net to client
       1782  bytes received via SQL*Net from client
        129  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1920  rows processed


然后将这些数据进行有效的排序,使得数据的存储相对集中,然后看看结果是否走索引!!!

23:43:21 SQL> set autotrace off
23:43:33 SQL> select count(*) from t2;

  COUNT(*)
----------
     26880

23:43:36 SQL> truncate table test_clu_factor;

表被截断。

23:43:56 SQL> select count(*) from test_clu_factor;

  COUNT(*)
----------
         0
23:44:09 SQL> insert into test_clu_factor select * from t2;

已创建26880行。

23:44:24 SQL> commit;

提交完成。

23:46:24 SQL> select ta.NUM_ROWS,ta.BLOCKS,ta.AVG_ROW_LEN,ind.clustering_factor from dba_tables ta,dba_indexes ind
23:52:49   2     where ta.TABLE_NAME=ind.table_name and ind.table_name=upper('test_clu_factor');

  NUM_ROWS     BLOCKS AVG_ROW_LEN CLUSTERING_FACTOR
---------- ---------- ----------- -----------------
     26880        180          40               170

通过刚刚对表进行排序后在插入,使得表的数据相对集中,索引数据块的值和集簇因子相差不大,所以应该走索引扫描才对!!

23:46:03 SQL> select * from test_clu_factor where ename='SCOTT';

已选择1920行。

执行计划
----------------------------------------------------------
Plan hash value: 4046700138

--------------------------------------------------------------------------------
---------------
| Id  | Operation                   | Name            | Rows  | Bytes | Cost (%C
PU)| Time     |
--------------------------------------------------------------------------------
---------------
|   0 | SELECT STATEMENT            |                 |  1920 | 61440 |    19
(0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST_CLU_FACTOR |  1920 | 61440 |    19
(0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | CLUTER_IND      |  1920 |       |     6
(0)| 00:00:01 |
--------------------------------------------------------------------------------
---------------

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

   2 - access("ENAME"='SCOTT')

统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        274  consistent gets
          0  physical reads
          0  redo size
      97972  bytes sent via SQL*Net to client
       1782  bytes received via SQL*Net from client
        129  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1920  rows processed

这次的查询走的索引范围扫描,和我们的猜测一样。


这就解释了为什么表有索引,但是确不走索引!!


你可能感兴趣的:(oracle)