|
从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
--删除IID为84的大部分记录,仅仅剩下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 小得多,我们在调优中,一般还是要以降低一致读为原则。总之,复杂的问题,化繁为简,具体问题具体研究,没有绝对正确的公式。
|
一共有 0 条评论