首页 > 其他 > 详细

【Cost】全表扫描时cost的计算(非工作量模式下-noworkload)

时间:2014-03-07 04:30:47      阅读:609      评论:0      收藏:0      [点我收藏+]
一、理论基础
CBO生成执行计划时需要估算cost值(成本计算),而这种估算是利用对象和系统统计信息,使用特定的公式算法来计算得出的。我们看看使用非工作量统计信息(noworkload)进行全表扫时,CBO是如何计算的。

1. 在接下来的讨论中,我们将会用到以下参数和字段名,这里需要知道:
参数:
    db_file_multiblock_read_count :定义在全表扫时一次可以最多读取的数据块数,在OLTP系统中建议在4-16,太大并没有益处。
    db_block_size:数据库块的大小

字段名(来自aux_stats$表):
1)Noworkload Statistics Stored in the Data Dictionary
Name                              Description
CPUSPEEDNW                The number of operations per second (in millions) that one CPU is able to process.
IOSEEKTIM                      Average time (in milliseconds) needed to locate data on the disk. The default value is 10. 
IOTFRSPEED                    Average number of bytes per millisecond that can be transferred from the disk. The default value is 4,096.

2) Workload Statistics Stored in the Data Dictionary
Name                             Description
CPUSPEED                     The number of operations per second (in millions) that one CPU is able to process
SREADTIM                     Average time (in milliseconds) needed to perform a single-block read operation
MREADTIM                    Average time (in milliseconds) needed to perform a multiblock read operation
MBRC                             Average number of blocks read during a multiblock read operation
MAXTHR                        Maximum I/O throughput (in bytes per second) for the whole system
SLAVETHR                      Average I/O throughput (in bytes per second) for a parallel processing slave
统计信息有什么用:例如记录IO的寻道时间、IO传送数据的速度、CPU处理速度这些信息可以用于CBO预估执行计划所做的消耗,进而确定执行计划。

3)其它:
#SRds - number of single block reads
#MRds - number of multi block reads
    
2. 总costio_cost+ cpu_cost/(cpuspeednw*sreadtim*1000)
CBO计算cost(成本)时需要用IO的开销加上CPU的开销,这样的加法是符合我们的逻辑的,但二者的计算单位不同,不能直接相加得出总的cost。这需要将cpu_cost转换为每秒钟可以执行的单块读的数量(这种转换是等价的),"cpu_cost/(cpuspeednw sreadtim*1000)”式子中的分母就是用来实现这样的一个转换(每个字符串的含义在1.中有解释)。接下来我们逐步公式中分析每一个分子式是如何计算得出的。
note:上述算法是使用非工作量统计信息时的值,如果使用工作量的统计信息时需要用cpuspeed替代cpuspeednw。

3. io_cost
io_cost=#SRds+(#MRds*mreadtim) / sreadtim
io_cost开销按照正常的逻辑思考应为:单块读的次数#SRds)+多块读的次数#MRds),块读的代价显然和块读的代价不同,所以查询优化器通常根据单块读来换算开销。因此我们需要将多块读转换为块读(这种转换同样应该是等价的):(#MRds*mreadtim) / sreadtim:
所以,io_cost=#SRds+(#MRds*mreadtim) / sreadtim

#SRds以单块读方式读到的数据块数就是单块读的次数,这个值由CBO来决定。
#MRds 
1)使用非工作量系统统计信息时:
     #MRds=Blocks/db_file_multiblock_read_count(即多块读的次数除以每次读的数据块数量)
2)使用工作量统计信息时:
    #MRds=Blocks/mbrc (mbrc:多块读时平均每次读的数据块的数量,因为不可能每次多块读的数量都是db_file_multiblock_read_count参数定义的值。但使用非工作量统计信息时,aux_stats$表中mbrc字段值为空,所以只能使用db_file_multiblock_read_count参数替代) 

sreadtim:
1)使用非工作量系统统计信息时:
    sreadtim=ioseektim+db_block_size/iotfrspeed
2)使用工作量统计信息时:
    在aud_stats$表中sreadtime字段的值
              
mreadtim:
1)使用非工作量系统统计信息时:
    mreadtim=ioseektim+(db_file_multiblock_read_count *db_lock_size)/iotfrspeed
2)使用工作量系统统计信息时:
    aud_stats$表中mreadtim字段的值,但以下两种情况下CBO不在使用aud_stats$表中的值
  1.     当sreadtim、mreadtim或mbrc不可用时
  2.     当mreadtim小于或等于sreadtim时
    此时cbo将使用前面的sreadtim公式计算sreadtim,使用如下的公式计算mreadtim
         mreadtim=ioseektim+(mdrc *db_lock_size)/iotfrspeed
note:其实对于使用非工作量统计信息和工作量统计信息时,计算公式的逻辑是一样,只是二者在aux_stats$表中对应的字段不同,所以应灵活替换。接下来的探讨中,将不在讨论使用工作量系统统计信息的情况,全部是使用非工作量统计信息时的情况。

到此,得知IO的消耗应为(使用非工作量统计信息):
io_cost= #SRds (#MRds*mreadtim) sreadtim
#MRds=Blocks/db_file_multiblock_read_count
sreadtime=ioseektim+db_block_size/iotfrspeed
mreadtim=ioseektim+(db_file_multiblock_read_count *db_lock_size)/iotfrspeed

后三者的计算公式中所用的多数分子式都可以在aud_stats$视图中查到,db_block_size,db_file_multiblock_read_count 则是初始化参数,Blocks则是在一个执行计划中将扫描的块数,由CBO根据对象的统计信息估算确定。同时我们可以发现,在使用非工作量统计信息时,db_file_multiblock_read_count在公式中以分母的形式存在,如果该参数设置过大,io_cost值会很小,CBO认为多块读的代价很小,在选择执行计划时会更倾向于使用全表扫描,影响执行高效的执行计划的产生。

4.  cpu_cost/(cpuspeednw* sreadtim*1000)
cpu_cost:执行计划的信息会存放在plan_table表中,而该表中的cpu_cost字段的值就是我们所需的值。
cpuspeeddnw:在aux_stat$表中可以查到该字段值,其含义在最开始已经解释。
sreadtime:在4.2中已经计算得出
note:上述是使用非工作量统计信息下的公式,如果使用工作量统计信息,则用cpuspeed替代cpuspeednw.

5. 总结:
最后我们可以得出总的消耗为:
    1)使用非工作量的统计信息时:
       cost=io_cost+ cpu_cost/(cpuspeednw*sreadtim*1000)
    2)使用工作量的统计信息时:
        cost=io_cost+ cpu_cost/(cpuspeed*sreadtim*1000)

二、实验验证:

1.版本信息

 SQL> select * from v$version;

BANNER

----------------------------------------------------------------

Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bi

 

2. 构造环境

创建mssm的表空间

SQL> create tablespace ballontt datafile ‘/u01/app/oracle/oradata/BALLONTT/ballontt01.dbf‘ size 30m autoextend on maxsize 100m uniform size 1m segment space management manual blocksize 8k;

 

建表及设置属性

SQL> create table t tablespace ballontt as select * from dba_objects where 1=0;

Table created.

SQL> alter table t pctfree 99 pctused 1;

Table altered.

 

插入值,并确保每个数据块上只有一行值

SQL> insert into t select * from dba_objects where rownum<2;

1 row created.

SQL> alter table t minimize records_per_block;

Table altered.

SQL>  insert into t select * from dba_objects where rownum<1000;

999 rows created.

SQL> commit;

Commit complete.

 

收集表的统计信息

SQL> begin

  2  DBMS_STATS.GATHER_TABLE_STATS(

  3  ownname => ‘SYS‘,

  4  tabname => ‘T‘,

  5  estimate_percent => 100,

  6  method_opt => ‘for all columns size 1‘,

  7  degree => DBMS_STATS.AUTO_DEGREE,

  8  cascade=>TRUE

  9  );

 10  end;

 11  /

PL/SQL procedure successfully completed.

 

SQL> select owner,table_name,blocks from dba_tables where table_name=‘T‘ and owner=‘SYS‘;

OWNER                          TABLE_NAME                         BLOCKS

------------------------------ ------------------------------ ----------

SYS                            T                                    1000

 

相关参数查看

SQL> show parameter db_file_multiblock_read_count;

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

db_file_multiblock_read_count        integer     16


SQL> show parameter db_block_size;

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

db_block_size                        integer     8192

 

3. 收集非工作量统计信息

SQL> begin

  2  DBMS_STATS.GATHER_SYSTEM_STATS (

  3  ‘NOWORKLOAD‘);

  4  end;

  5  /

PL/SQL procedure successfully completed.

 

4. 查看cost

SQL> set autot on

SQL> select count(*) from t;

  COUNT(*)

----------

      1000

Execution Plan

----------------------------------------------------------

Plan hash value: 2966233522

 

-------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

-------------------------------------------------------------------

|   0 | SELECT STATEMENT   |      |     1 |   171   (0)| 00:00:04 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T    |  1000 |   171   (0)| 00:00:04 |

-------------------------------------------------------------------

5. 查看aud_stats$

SQL> select pname, pval1 from sys.aux_stats$ where sname=‘SYSSTATS_MAIN‘;

PNAME                               PVAL1

------------------------------ ----------

CPUSPEED

CPUSPEEDNW                       1740.872

IOSEEKTIM                          15.526

IOTFRSPEED                           4096

MAXTHR

MBRC

MREADTIM

SLAVETHR

SREADTIM

只有cpuspeednw,ioseektim,iotfrspeed三个字段有值,据此判断只能使用非工作量的统计信息。

#SRds=0,因为是全表扫描,单块读为0

#MRds=表的块数/多块读参数=1000/16=62.5

NOTE: 如果没有收集过系统统计信息,那么Oracle采用非工作量统计,如果收集了,Oracle采用工作量统计的计算方法


6. 开始计算:

cost=io_cost+ cpu_cost/cpuspeednw*sreadtim*1000

   = [#SRds (#MRds*mreadtim) sreadtim]+ cpu_cost/cpuspeednw*sreadtim*1000

其中#SRds=0

       #MRds=1000/16=62.5

        mreadtim= ioseektim+db_file_multiblock_read_count *db_lock_size/iotfrspeed

                     =15.256+(16*8192)/4096

                     =47.256

       Sreadtim= ioseektim+db_block_size/iotfrspeed

              =15.526+8192/4096

              =17.526

       Cpu_cost=SQL> explain plan for select count(*) from t;  

                          Explained.

                          SQL> select cpu_cost from plan_table;

                          CPU_COST

                           ----------

                       = 7271440

       Cpuspeednw=1740.872

最终COST=[0+(62.5*47.256)/ 17.526]+ 7271440/(1740.872*17.526*1000)= 168.75938≈ 169


169和统计出的cost近似相等(其实还是有点问题,四舍五入后应为170就准确了),据此我们知道知道了全表扫描时(使用非工作量统计信息)的cost的计算方法。同时,在一、中也给出了使用工作量统计时cost的算法,可以按照此实验思路进行验证。

Note:为什么我说四舍五入后应该是170的?170和统计出的171还差1,因为oracle使用了隐含参数_table_scan_cost_plus_one,该参数使得在table full scanindex fast full scan的时候会将cost+1


ballontt
2014/03/06

---The End---
如需转载,请标明出处和链接,谢谢!

【Cost】全表扫描时cost的计算(非工作量模式下-noworkload),布布扣,bubuko.com

【Cost】全表扫描时cost的计算(非工作量模式下-noworkload)

原文:http://blog.csdn.net/ballontt/article/details/20649235

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!