日历

2008 7.6 Sun
  12345
6789101112
13141516171819
20212223242526
2728293031  
«» 2008 - 7 «»

文章搜索

日志文章

2008年05月08日 16:38:17

从COST的角度看:为何不使用索引一例

COST的角度看:为何不使用索引一例.


Wangwj/valenwon
Scttsc/cnoug
2008- 05- 08


在论坛有网友问
http://www.oracle.com.cn/viewthread.php?tid=134090&extra=page%3D1
问为什么,两个索引都在谓词中引用,Oracle是怎么选择的呢?我知道Oracle是根据COST来选择合适的执行计划的,为了深入了解其机制,我做了一个测试过程如下。
-- Create table
create table TEST
(
EN_NAME VARCHAR2(
6
),
BS_CODE VARCHAR2(
10
),
DDATE   DATE,
IID   NUMBER
)

-- Create/Recreate indexes
create index TEST_DATE on TEST (DDATE)

create index TEST_ID on TEST (IID)

--
IID和时间DDATE建普通索引

--插入了11万数据

INSERT INTO test t(iid,ddate)
SELECT trunc(dbms_random.value(
0,100)),SYSDATE -trunc(dbms_random.value(0,100
)) FROM dual
CONNECT BY ROWNUM <
110001

--删除IID84的大部分记录,仅仅剩下23

Delete from test where iid=84 and rownum < 1089

--为了保证信息的准确性,我重建索引,降低HWL,分析表

Alter index test_id rebuild;

Alter index teset_date rebuild;



alter table test shrink space cascade;



analyse table test analyze statistics;
SQL> exec show_space('TEST','APS2');
Unformatted Blocks .....................           0
FS1 Blocks (0-25) .....................           1
FS2 Blocks (25-50) .....................           1
FS3 Blocks (50-75) .....................           0
FS4 Blocks (75-100).....................           0
Full Blocks     .....................         269
Total Blocks............................         288
Total Bytes.............................     2,359,296
Total MBytes............................           2
Unused Blocks...........................           3
Unused Bytes............................       24,576
Last Used Ext FileId....................         10
Last Used Ext BlockId...................       8,073
Last Used Block.........................         29


PL/SQL procedure successfully completed.
--运行SQL

SELECT * FROM TEST WHERE IID=84

SQL> SELECT * FROM TEST WHERE IID=84;

23 rows selected.

--------------------------------------------------------------------------

| Id | Operation       | Name | Rows | Bytes | Cost (%CPU)| Time   |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT |     | 1089 | 11979 |   73   (6)| 00:00:01 |

|* 1 | TABLE ACCESS FULL| TEST | 1089 | 11979 |   73   (6)| 00:00:01 |

--------------------------------------------------------------------------

Statistics

----------------------------------------------------------

      0 recursive calls

      0 db block gets

    277 consistent gets

居然是TABLE ACCESS FULL?注意COST 73 ,277的一致读

根据常识,当访问小于表15%的数据的时候,走索引会获得更好的效率。为什么这里会走FULL SCAN呢?

那么再运行一次走索引的,如下:

SQL> SELECT /*+ INDEX(test TEST_ID)*/* FROM test WHERE iid=84;



23 rows selected.

---------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT         |       | 1089 | 11979 |   269   (0)| 00:00:04 |

|   1 | TABLE ACCESS BY INDEX ROWID| TEST   | 1089 | 11979 |   269   (0)| 00:00:04 |

|* 2 |   INDEX RANGE SCAN       | TEST_ID | 1089 |     |   3   (0)| 00:00:01 |

Statistics

----------------------------------------------------------

      1 recursive calls

      0 db block gets

      13 consistent gets

COST 269,13的一致读。



这样就很清楚,优化器认为全表扫描的成本较小,所以它选择FULL SCAN,虽然效率不一定会优于走索引。

--下面我来简单说明一下COST是如何计算的呢

从索引访问说起,先收集一些基本信息

SQL> SELECT t.blevel,t.leaf_blocks FROM User_Indexes t WHERE index_name='TEST_ID';



  BLEVEL LEAF_BLOCKS

---------- -----------

1                     213

2级索引,有213个叶子块。

根据公式:

cost =
blevel +
ceiling(leaf_blocks * effective index selectivity) +
ceiling(clustering_factor * effective table selectivity)

blevel = 1

leaf_block=213

effective index selectivity = 0.01

有效索引选择率,即基于谓词所希望返回函数的比例,这里是0.01,因为我们采用的0~99的随机IID,虽然我们删除了大量IID为84的数据,但是统计信息里面还是认为没一个单独值的密度为0.01(除非你创建合适的CUBE)。

clustering_factor 集群因素 ,是一个用于衡量索引有序度和表的混乱度值,关于什么是集群因素,请参考文章:

http://valen.blog.ccidnet.com/blog-htm-do-showone-uid-51502-type-blog-itemid-207620.html

或者看Cost Based Oracle Fundamentals一书,4.2.3

这里我只能用SQL计算一个值

SQL> SELECT COUNT(*) FROM (

2 SELECT COUNT(1),iid,blk FROM (

3 sELECT t.*,dbms_rowid.rowid_block_number(ROWID) blk from test t ORDER BY iid)

4 GROUP BY iid,blk ORDER BY iid

5 )

6 /



COUNT(*)

----------

  26414

effective table selectivity = 0.01 数据密度

那么COST=1+CEIL(213*0.01)+CEIL(26414*0.01)= 269

非常完美的一致!

再看FULL SCAN的计算方式

传统的计算方式,COST = FULL BLOCK / adjusted MULTPLEBLOCK READ

我的db_file_multiblock_read_count为16,这里算出

Adjusted db_file_multiblock_read_count 为3.64,偏低。但这并不重要,暂不研究。

最后,我们设置10053事件来追踪优化器的行为

SQL> alter session set events '10053 trace name context forever,level 2';



Session altered.



打开trace文件,在里面找到一段我们感兴趣的内容

***********************

Table Stats::

Table: TEST Alias: TEST

  #Rows: 108902 #Blks: 306 AvgRowLen: 16.00

Index Stats::

Index: TEST_DATE Col#: 3

  LVLS: 1 #LB: 289 #DK: 200 LB/K: 1.00 DB/K: 134.00 CLUF: 26804.00

Index: TEST_ID Col#: 4

  LVLS: 1 #LB: 213 #DK: 100 LB/K: 2.00 DB/K: 265.00 CLUF: 26594.00

***************************************

SINGLE TABLE ACCESS PATH

Column (#4): IID(NUMBER)

  AvgLen: 2.00 NDV: 100 Nulls: 0 Density: 0.01 Min: 0 Max: 99

Table: TEST Alias: TEST  

  Card: Original: 108902 Rounded: 1089 Computed: 1089.02 Non Adjusted: 1089.02

Access Path: TableScan

  Cost: 64.56 Resp: 64.56 Degree: 0

    Cost_io: 61.00 Cost_cpu: 30493681

    Resp_io: 61.00 Resp_cpu: 30493681

Access Path: index (AllEqRange)

  Index: TEST_ID

  resc_io: 269.00 resc_cpu: 2363417

  ix_sel: 0.01 ix_sel_with_filters: 0.01

  Cost: 269.28 Resp: 269.28 Degree: 1

Best:: AccessPath: TableScan

      Cost: 64.56 Degree: 1 Resp: 64.56 Card: 1089.02 Bytes: 0



优化器得出结论:Best:: AccessPath: TableScan

相同的方法,也可以解释,优化器为什么使用这个索引,不使用那个索引。这里我就不继续做试验了。优化器根据COST做出判断也不一定是对了,这里案例中,走索引的一致读比FULL SCAN 小得多,我们在调优中,一般还是要以降低一致读为原则。总之,复杂的问题,化繁为简,具体问题具体研究,没有绝对正确的公式。

Tags: oracle   optimizer  

类别: Tuning |  评论(0) |  浏览(2048) |  收藏
发表评论