在基于cbo的优化器模式下面,oracle会把sql语句的各种执行计划的成本做比较,取其最小的作为其最优执行计划,同时,在决定是用index scan 还是full table scan 访问一个表的时候,oracle会把index scan的执行成本转化为fts的成本,然后再根据其成本大小来决定执行计划,这个index scan 和full table scan 的比值即为 optimizer_index_cost_adj,这个初始化参数代表一个百分比,取值范围在1到10000之间.该参数表示索引扫描和全表扫描成本的比较。缺省值100表示索引扫描成本等价转换与全表扫描成本。
下面的例子具体说明了该参数的不同取值对执行计划的影响(下面的例子不能做为性能调整的根据,只是作为该参数的一个研究型文档,实际在调整该参数的时候,还是参考应用的类型(oltp/olap),及v$system_event 中的db
file sequential read,db file scattered read的具体数值。
建立测试表
SQL> create table t as select * from dba_objects;
SQL> create index t_idx on t(owner);
SQL> SHOW PARAMETER OPTIMIZER_INDEX_COST_ADJ
NAME TYPE VALUE
------------------------------------ ----------- -----
optimizer_index_cost_adj integer 100
SQL> SHOW PARAMETER DB_FILE
NAME TYPE VALUE
------------------------------------ ----------- -----
db_file_multiblock_read_count integer 128
SQL> set autotrace traceonly explain;
SQL> select * from t where owner='HR';
Execution Plan
----------------------------------------------------------
Plan hash value: 1579008347
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| 2500 |505K|92
(0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 2500 |505K|92
(0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | T_INX | 2500 |
| 8 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='HR')
Note
-----
- dynamic sampling used for this statement (level=2)
专门走一次全表扫描,看下cost是多少:
SQL> select /*+ FULL(T) */ * FROM T WHERE owner='HR';
SQL> select /*+ FULL(T) */ * FROM T WHERE owner='HR';
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT || 2500 | 505K| 1591
(1)| 00:00:20 |
|* 1 | TABLE ACCESS FULL| T| 2500 | 505K| 1591 (1)| 00:00:20 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='HR')
Note
-----
- dynamic sampling used for this statement (level=2)
默认的optimizer_index_cost_adj
=100,全表扫描的成本是1591,索引范围扫描的成本92,此时索引扫描的成本要比全表扫描的成本低,调整optimizer_index_cost_adj 参数,调整的基点: 全表扫描成本/索引扫描成本
SQL> SET AUTOTRACE OFF;
SQL> SELECT 1591/92*100 FROM DUAL;
1591/92*100
-----------
1729.34783
接下来,是重点,我们取一个比这个基数略大的数值:1731
SQL> ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ=1731;
Session altered.
SQL>
SHOW PARAMETER OPTIMIZER_INDEX_COST_ADJ
NAME TYPE
VALUE
------------------------------------ ----------- ------------------------------
optimizer_index_cost_adj integer1731
SQL>
set autotrace traceonly explain;
SQL> select * from t where owner='HR';
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 | 505K| 1591 (1)| 00:00:20 |
|* 1 | TABLE ACCESS FULL| T | 2500 | 505K| 1591 (1)| 00:00:20 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='HR')
Note
-----
- dynamic sampling used for this statement (level=2)
可以看到,此时奇怪的事情发生了,这个查询居然是走的全表扫描,而不是索引,这个参数到底是如何决定的呢?
原来: 在比较的时候,Oracle会把索引扫描的成本转换为全表扫描的成本,和全表扫描的COST进行比较.这个转换需要一个转换因子.就是optimizer_index_cost_adj:
optimizer_index_cost_adj * (Index Scan Cost) = 等价的 Full Scan Cost(比如刚才1731*92=159252 > 1591*100,所以选择了全表扫描,因为这个参数的权重让oracle决定走全表扫描的cost更低)
这个 等价的 Full Scan Cost 就是来和全表扫描成本进行比较的.
而这个转换因子的临界值实际上就是Full Scan Cost 和 Index Scan Cost的比值.
即:
optimizer_index_cost_adj = Full Scan Cost / Index Scan Cost(刚才1591/92 *100 =1731)
然后,我们把这个因子降低一点,看看是不是就会走索引呢?继续实验:
SQL>
ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ=1725;
Session altered.
SQL> select * from t where owner='HR';
Execution Plan
----------------------------------------------------------
Plan hash value: 1579008347
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2500 |
505K| 1589 (1)| 00:00:20 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 2500 |505K| 1589 (1)| 00:00:20 |
|* 2 | INDEX RANGE SCAN | T_INX | 2500 | |
138 (0)| 00:00:02 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='HR')
Note
-----
- dynamic sampling used for this statement (level=2)
果然,此时又走的索引,但是cost变成了1589(我们知道full table scan的成本是1591),所以选择了成本较少的,那么这个值是怎么来的呢?
是实际的cost*optimizer_index_cost_adj 得到的(1589/1725=0.9211=92/100)
综上,我们明白了这个参数可以理解为一个权重的值,这个值越高,index scan的权重越小,越倾向于使用full table scan。一般OLTP系统里,建议设置较小的值10-50,这样就会有更多的查询走索引扫描,参考http://www.dba-oracle.com/oracle_tips_cost_adj.htm
For some OLTP systems, re-setting this parameter to a smaller value (between 10- to 30) may result in huge performance gains