您的位置:首页 > 数据库 > SQL

采用optimizer_index_cost_adj优化SQL语句

2004-07-26 16:51 926 查看
我们知道,oracle有的时候,可能会因为统计信息的不准确或者是优化器的问题,导致语句选择错误的执行计划,如本来该走索引扫描的,但是采用了全表扫描。对于很多系统更趋向使用索引扫描,因为往往实际上,索引扫描的代价的确是小于全表扫描的。。。
我们看一个实际的例子
SQL> set autot trace exp
SQL> select count(*) from auction_auctions a, auction_bids b
2 where a.id=b.auction and auction_type='a'
3 and ends>to_date('2004-05-21','yyyy-mm-dd')
4 and approve_status>=0;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=65151 Card=1 Bytes=80)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=65151 Card=955916 Bytes=76473280)
3 2 VIEW OF 'index$_join$_001' (Cost=64015 Card=95048 Bytes=4467256)
4 3 HASH JOIN
5 4 HASH JOIN
6 5 PARTITION RANGE (ITERATOR)
7 6 INDEX (FAST FULL SCAN) OF 'IND_AUCTION_ZOO_CLO_STA_CAT' (NON-UNIQUE) (Cost=8713 Card=95048 Bytes=4467256)
8 5 PARTITION RANGE (ITERATOR)
9 8 INDEX (FAST FULL SCAN) OF 'PK_AUCTION_AUCTIONS_ID' (UNIQUE) (Cost=8713 Card=95048 Bytes=4467256)
10 4 PARTITION RANGE (ITERATOR)
11 10 INDEX (FAST FULL SCAN) OF 'IND_AUC_USER_CLO_TYP_BID_STA' (NON-UNIQUE) (Cost=8713 Card=95048 Bytes=4467256)
12 2 INDEX (FAST FULL SCAN) OF 'IND_BIDS_AUCTIONSTATUS' (NON-UNIQUE) (Cost=397 Card=955920 Bytes=31545360)

在以上的例子中,对该表的一个查询,采用了索引快速扫描的方法,并通过hash连接多个扫描结果,对于一个非常大的表,查询时间在10s以下。
但是,当我们修改查询条件为如下时:

SQL> select count(*) from auction_auctions a, auction_bids b
2 where a.id=b.auction and auction_type='a'
3 and ends>to_date('2004-07-01','yyyy-mm-dd')
4 and approve_status>=0;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=35001 Card=1 Bytes=80)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=35001 Card=807907 Bytes=64632560)
3 2 PARTITION RANGE (ITERATOR)
4 3 TABLE ACCESS (FULL) OF 'AUCTION_AUCTIONS' (Cost=33888 Card=68567 Bytes=3222649)
5 2 INDEX (FAST FULL SCAN) OF 'IND_BIDS_AUCTIONSTATUS' (NON-UNIQUE) (Cost=397 Card=955920 Bytes=31545360)

这个时候,我们可以看到,Oracle采用了全表扫描,虽然这个查询语句所查询的记录数比上个查询的记录数要少,但是因为这个表是已ends分区的分区表,当ends>07-01时,只需要在一个分区中查询即可,Oracle错误的计算了成本(Cost=35001,远远小于上面采用索引的成本Cost=65151),所以采用在分区范围内的全表扫描。
但是实际情况是,该查询需要>10分钟才能获得查询结果。显然,这里oracle计算成本的时候是有问题的,这里的全表扫描的速度远远低于索引扫描的时间。
那么,我们有什么办法能让oracle回到索引扫描的执行计划呢?我们首先考虑hint。
SQL> select /*+ index(a IND_AUCTION_ZOO_CLO_STA_CAT) index(a PK_AUCTION_AUCTIONS_ID) index(a IND_AUC_USER_CLO_TYP_BID_STA) */
2 count(*) from auction_auctions a, auction_bids b
3 where a.id=b.auction and auction_type='a'
4 and ends>to_date('2004-07-21','yyyy-mm-dd')
5 and approve_status>=0;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=143358 Card=1 Bytes=80)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=143358 Card=807907 Bytes=64632560)
3 2 PARTITION RANGE (ITERATOR)
4 3 TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'AUCTION_AUCTIONS' (Cost=142245 Card=68567 Bytes=3222649)
5 4 INDEX (SKIP SCAN) OF 'IND_AUC_USER_CLO_TYP_BID_STA' (NON-UNIQUE) (Cost=28314 Card=152804)
6 2 INDEX (FAST FULL SCAN) OF 'IND_BIDS_AUCTIONSTATUS' (NON-UNIQUE) (Cost=397 Card=955920 Bytes=31545360)

可以看到,不管是怎么指定索引,Oracle只能是很死板的选用一个它认为最快的索引,而不会使用上面的比较优化的多个索引快速扫描的方法,而且通过指定的索引扫描,oracle计算出来的成本也很大(Cost=143358),实际上,该语句的执行也是非常慢,甚至慢于分区全表扫描。

有什么办法,能让oracle最简单的使用上面的执行计划呢?我们可以考虑参数OPTIMIZER_INDEX_COST_ADJ,这个初始化参数代表一个百分比,取值范围在1到10000之间。该参数表示索引扫描和全表扫描成本的比较,缺省值100表示索引扫描成本等价转换与全表扫描成本。
我们修改该参数值
SQL> alter session set optimizer_index_cost_adj = 40;
Session altered.
SQL> select count(*) from auction_auctions a, auction_bids b
2 where a.id=b.auction and auction_type='a'
3 and ends>to_date('2004-07-01','yyyy-mm-dd')
4 and approve_status>=0;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=30505 Card=1 Bytes=80)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=30505 Card=807907 Bytes=64632560)
3 2 VIEW OF 'index$_join$_001' (Cost=29391 Card=68567 Bytes=3222649)
4 3 HASH JOIN
5 4 HASH JOIN
6 5 PARTITION RANGE (ITERATOR)
7 6 INDEX (FAST FULL SCAN) OF 'IND_AUCTION_ZOO_CLO_STA_CAT' (NON-UNIQUE) (Cost=3484 Card=68567 Bytes=3222649)
8 5 PARTITION RANGE (ITERATOR)
9 8 INDEX (FAST FULL SCAN) OF 'PK_AUCTION_AUCTIONS_ID' (UNIQUE) (Cost=3484 Card=68567 Bytes=3222649)
10 4 PARTITION RANGE (ITERATOR)
11 10 INDEX (FAST FULL SCAN) OF 'IND_AUC_USER_CLO_TYP_BID_STA' (NON-UNIQUE) (Cost=3484 Card=68567 Bytes=3222649)
12 2 INDEX (FAST FULL SCAN) OF 'IND_BIDS_AUCTIONSTATUS' (NON-UNIQUE) (Cost=397 Card=955920 Bytes=31545360)

这个时候,我们发现,oracle又选择了我们所希望的执行计划,在这个执行计划中,成本是30505
我们可以得到,Oracle认为走该执行计划的原始成本是
(30505/40)*100=76263
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: