测试6——观察Optimizer_index_cost_adj 对执行计划的影响

    在基于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 readdb 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

上一篇:C注释 转换为 C++注释


下一篇:看动画学算法之:平衡二叉搜索树AVL Tree