Chinaunix首页 | 论坛 | 博客
  • 博客访问: 154671
  • 博文数量: 39
  • 博客积分: 825
  • 博客等级: 准尉
  • 技术积分: 955
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-25 14:05
文章分类

全部博文(39)

文章存档

2014年(4)

2013年(13)

2012年(22)

我的朋友

分类: Oracle

2012-11-06 19:57:23

【建立3000行测试表】

DROP TABLE t1 PURGE;

CREATE TABLE t1(v1,n1,n2) 
  AS 
SELECT to_char(MOD(rownum, 20)), rownum, MOD(rownum, 20)
FROM   dual
CONNECT BY LEVEL <= 3000;

BEGIN
  dbms_stats.gather_table_stats(ownname          => USER,
                                tabname          => 't1',
                                estimate_percent => 100,
                                method_opt       => 'for all columns size 1');
END;
/


【成本估算】

  这里使用ordered_predicates强制进行谓词排序:

DELETE FROM plan_table;

EXPLAIN PLAN SET STATEMENT_ID='v1' FOR
SELECT /*+ ordered_predicates*/
 *
FROM   t1
WHERE  v1 = 1 AND
       n2 = 18 AND
       n1 = 998;

EXPLAIN PLAN SET STATEMENT_ID='n1' FOR
SELECT /*+ ordered_predicates*/
 *
FROM   t1
WHERE  n1 = 998 AND
       n2 = 18 AND
       v1 = 1;

EXPLAIN PLAN SET STATEMENT_ID='v1c' FOR       
SELECT /*+ ordered_predicates*/
 *
FROM   t1
WHERE  v1 = '1' AND
       n2 = 18 AND
       n1 = 998;

####################################################################
       
  PS:记得看过donald的一本性能调优的书,貌似8i的谓词排序hint是加在
WHERE后的,但是9i之后也直接放到select后中,据说ordered_predicates 
还受_pred_move_around隐式参数的影响:

SELECT ksppinm, ksppstvl, ksppdesc
FROM   x$ksppi a, x$ksppcv b
WHERE  a.indx = b.indx AND
       a.ksppinm = '_pred_move_around';
--_pred_move_around TRUE enables predicate move-around

  不过我在10.2.0.1中会话级别将_pred_move_around设置为false后,ORDER
ed_predicates依旧起作用,感兴趣的可以将兼容版本放到9.2.0测试下,这里
不再追究。

####################################################################


【查看成本估算值】

SELECT statement_id, io_cost, cpu_cost, cost FROM plan_table WHERE id = 0;
/*
v1 3 1091968 3
n1 3 784150 3
v1c 3 791968 3
*/

  我们主要关注cpu_cost部分。
  

【查看执行计划】  

SELECT * FROM TABLE(dbms_xplan.display(NULL, 'n1', 'typical', NULL));
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     9 |     4  (25)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     9 |     4  (25)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("N1"=998 AND "N2"=18 AND TO_NUMBER("V1")=1)



SELECT * FROM TABLE(dbms_xplan.display(NULL, 'v1', 'typical', NULL));
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     9 |     4  (25)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     9 |     4  (25)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(TO_NUMBER("V1")=1 AND "N2"=18 AND "N1"=998)    


  
SELECT * FROM TABLE(dbms_xplan.display(NULL, 'v1c', 'typical', NULL));   
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     9 |     4  (25)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     9 |     4  (25)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("V1"='1' AND "N2"=18 AND "N1"=998)


【cpu消耗操作项目解释】

  这里与上面执行计划顺序对应:

#####################################
n1

  产生cpu开销的操作:    
    3000次数值比较,产生1行
    这一行进行一次n2数值比较
    这一行强制转换一次并比较一次
    
  综合:
    1次转换
    3002次数值比较    
    cpu开销:784150
    
#####################################
v1
       
  产生cpu开销的操作:
    3000次v1转换,并应用过滤条件,产生150行
    150次n2比较,产生8行(对于为什么cbo会认为返回8行,后面将解释)
    8次n1比较
    
  综合:
    3000次转换      
    3158次数值比较
    cpu开销:1091968
    
#####################################
v1c
           
  产生cpu开销的操作:    
    3000次数值比较,产生150行
    这150行进行150次n2数值比较,产生8行
    这8行与n1进行数值比较
    
  综合:
    3158次数值比较
    cpu开销:791968


【基准测试数值观察结论】

  通过观察v1与v1c,我们发现v1相比v1c只是多出了3000次to_number操作,所以
我们很容易的得出一次to_number操作消耗的cpu调用是100次:

SELECT (1091968 - 791968) / 3000 to_number_cpucosting FROM dual;


【总结】

  cbo估算cpu成本是不同原子级别操作的cpu call总合,而这些“原子级”
的对象实在太多,它可能是一个函数,一个过滤操作等,而我们知道就
算是访问同一行的不同列,cpu开销也不一样;

  而且不同版本相同的单个原子操作可能成本开销也不一样,这和oracle
内核代码息息相关,比如上面的to_number,你可以假想它的内部算法是永
恒不变的,但是这种内稳态的东西实在太少。

  总之一句话,对于cpu成本估算细节方面的东西我们没必要纠结,而上面
的例子只是帮助你在cbo估算cpu成本方面时有一个直观的理解。
阅读(805) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~