博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

如何理解recursive calls,db block gets和consistent gets

Posted on 2012-08-29 17:48  徐正柱-  阅读(1155)  评论(0编辑  收藏  举报

在实际工作中经常要看某个sql语句的执行计划,例如:

在sqlplus使用命令SET AUTOTRACE ON后,执行计划显示如下:

SQL>set autotrace on

SQL> select count(*) from emp;

  COUNT(*)
----------
        12

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'EMP'
Statistics
----------------------------------------------------------
          0  recursive calls
          2  db block gets
          1  consistent gets
          0  physical reads
          0  redo size
        383  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
其中recursive calls,db block gets,consistent gets的具体含义是什么?

现整理出的具体解释如下:

· Recursive Calls. Number of recursive calls generated at both the user and system level.
Oracle Database maintains tables used for internal processing. When it needs to change these tables, Oracle Database generates an internal SQL statement, which in turn generates a recursive call.
In short, recursive calls are basically SQL performed on behalf of your SQL. So, if you had to parse the query, for example, you might have had to run some other queries to get data dictionary information. These would be recursive calls. Space management, security checks, calling PL/SQL from SQL—all incur recursive SQL calls.
· DB Block Gets. Number of times a CURRENT block was requested.
Current mode blocks are retrieved as they exist right now, not in a consistent read fashion.
Normally, blocks retrieved for a query are retrieved as they existed when the query began. Current mode blocks are retrieved as they exist right now, not from a previous point in time.
During a SELECT, you might see current mode retrievals due to reading the data dictionary to find the extent information for a table to do a full scan (because you need the "right now" information, not the consistent read). During a modification, you will access the blocks in current mode in order to write to them.
(DB Block Gets:请求的数据块在buffer能满足的个数)
· Consistent Gets. Number of times a consistent read was requested for a block.
This is how many blocks you processed in "consistent read" mode. This will include counts of blocks read from the rollback segment in order to roll back a block.
This is the mode you read blocks in with a SELECT, for example.
Also, when you do a searched UPDATE/DELETE, you read the blocks in consistent read mode and then get the block in current mode to actually do the modification.
(Consistent Gets:数据请求总数在回滚段Buffer中)
· Physical Reads. Total number of data blocks read from disk. This number equals the value of "physical reads direct" plus all reads into buffer cache. (Physical Reads:实例启动后,从磁盘读到Buffer Cache数据块数量)
· Sorts (disk). Number of sort operations that required at least one disk write. Sorts that require I/O to disk are quite resource intensive. Try increasing the size of the initialization parameter SORT_AREA_SIZE.

(Sorts(disk):从磁盘上进行排序的数量)

Physical Reads通常是我们最关心的,如果这个值很高,说明要从磁盘请求大量的数据到Buffer Cache里,通常意味着系统里存在大量全表扫描的SQL语句,这会影响到数据库的性能,因此尽量避免语句做全表扫描,对于全表扫描的SQL语句,建议增 加相关的索引,优化SQL语句来解决。

关于physical reads ,db block gets 和consistent gets这三个参数之间有一个换算公式:

数据缓冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )。

在SQL语句里体现如下:

用以下语句可以查看数据缓冲区的命中率:

SQL>SELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads');

查询出来的结果Buffer Cache的命中率应该在90%以上,否则需要增加数据缓冲区的大小。

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

b block gets:Number of times a CURRENT block was requested.
consistent gets:Number of times a consistent read was requested for a block.
physical reads:Total number of data blocks read from disk. This number equals the value of "physical reads direct" plus all reads into buffer cache.

也可查询:

select sid,value,name from v$sesstat x,v$statname y
where x.statistic#=y.statistic#
and ( name like '%db block%change%' or name like '%db block%get%'
or name like '%consistent%change%' or name like '%physical reads%'
or name like '%physical writes%' or name like '%scans%'
or name = 'index fetch by key' or name like '%read%'
)
and sid = 11 and value > 0
order by value

---------------------------------------------------------------
针对以上3个概念进行的说明解释及关系如下:
1、DB Block Gets(当前请求的块数目)
     当前模式块意思就是在操作中正好提取的块数目,而不是在一致性读的情况下而产生的块数。正常的情况下,一个查询提取的块是在查询开始的那个时间点上存在的数据块,当前块是在这个时刻存在的数据块,而不是在这个时间点之前或者之后的数据块数目。
2、Consistent Gets(数据请求总数在回滚段Buffer中的数据一致性读所需要的数据块)
      这里的概念是在处理你这个操作的时候需要在一致性读状态上处理多少个块,这些块产生的主要原因是因为由于在你查询的过程中,由于其他会话对数据块进行操 作,而对所要查询的块有了修改,但是由于我们的查询是在这些修改之前调用的,所以需要对回滚段中的数据块的前映像进行查询,以保证数据的一致性。这样就产 生了一致性读。
3、Physical Reads(物理读)
就是从磁盘上读取数据块的数量,其产生的主要原因是:
1、 在数据库高速缓存中不存在这些块
2、 全表扫描
3、 磁盘排序
它们三者之间的关系大致可概括为:
逻辑读指的是Oracle从内存读到的数据块数量。一般来说是'consistent gets' + 'db block gets'。当在内存中找不到所需的数据块的话就需要从磁盘中获取,于是就产生了'phsical reads'。

 

 

       首先介绍一下什么是consistent gets,我摘引一段官方的定义,就不做自己的解释了:
The consistent gets Oracle metric is the number of times a consistent read (a logical RAM buffer I/O) was requested to get data from a data block.
consistent gets在判断一段SQL的性能时非常有用,通常来讲比较两段SQL的性能好坏不是看谁的执行时间短,而是看谁的consistent gets小。不过这也不是绝对的,下面这个例子就是一个反例:
ETL@RACTEST> create table test( a int);


Table created.

Elapsed: 00:00:00.05
ETL@RACTEST> ETL@RACTEST> begin
   for i in 1..10000 loop
   insert into test values (i);
   end loop;
   end;
   /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.44
ETL@RACTEST> set autot trace
ETL@RACTEST> ETL@RACTEST> select * from test;


10000 rows selected.

Elapsed: 00:00:00.05

Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
 0 | SELECT STATEMENT       | 10000 |   126K|       (0)| 00:00:01 |
 1 |  TABLE ACCESS FULL| TEST | 10000 |   126K|       (0)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
          recursive calls
          db block gets
        690  consistent gets
          physical reads
          redo size
     214231  bytes sent via SQL*Net to client
       7791  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          sorts (memory)
          sorts (disk)
      10000  rows processed

可以看到select *读了690个内存块。

ETL@RACTEST> select * from test order by 1;

10000 rows selected.

Elapsed: 00:00:00.04

Execution Plan
----------------------------------------------------------
Plan hash value: 2007178810

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
 0 | SELECT STATEMENT        | 10000 |   126K|      (15)| 00:00:01 |
 1 |  SORT ORDER BY          | 10000 |   126K|      (15)| 00:00:01 |
 2 |   TABLE ACCESS FULL| TEST | 10000 |   126K|       (0)| 00:00:01 |
---------------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement

Statistics
----------------------------------------------------------
          recursive calls
          db block gets
         23  consistent gets
          physical reads
          redo size
     174288  bytes sent via SQL*Net to client
       7791  bytes received via SQL*Net from client
        668  SQL*Net roundtrips to/from client
          sorts (memory)
          sorts (disk)
      10000  rows processed

再看一下order by,竟然只有23个逻辑读!
       1. select * from test;
       2. select * from test order by 1;
       第1个SQL比第2个SQL效率高是毋庸置疑的。但是为什么第2个SQL的consistent gets如此之少,我起初也是百思不得其解,最终我在ASK TOM中找到了答案。原因有二:
       一:通常情况下,不在logical RAM buffer中的数据要通过physical reads来读取,而physical reads后通常会紧跟着一个consistent gets。因此一般情况下consistent gets是要比physical reads大的。但是有一个特例,如果physical reads得到的数据直接用于HASH或者SORT,则只记为physical reads不记为consistent gets。所以加上order by后有可能physical reads多但consistent gets少。不过这个原因不是我这里现象产生的原因,因为我这个实验里根本没有physical reads。
       二:arraysize的影响。arraysize是指读取数据时一次读取得到的行数。这个值默认为15,使用show arraysize命令可以查看。一个数据块例如有100条记录,那么并不是读取这个块一次就能取到所有数据,以arraysize=15为例,就要有100/15=7次consistent gets。把arraysize设置得大一点可以降低consistent gets,不过有时候可能会消耗更多的资源。如果我们做select count(0) from test;操作,那么Oracle会把arraysize暂时设为test的行数,因此consistent gets会很少:
ETL@RACTEST> select count(0) from test;

Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 1950795681

-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
 0 | SELECT STATEMENT            1 |       (0)| 00:00:01 |
 1 |  SORT AGGREGATE            1 |                     |
 2 |   TABLE ACCESS FULL| TEST | 10000 |       (0)| 00:00:01 |
-------------------------------------------------------------------

Note
-----
   - dynamic sampling used for this statement


Statistics
----------------------------------------------------------
          recursive calls
          db block gets
         23  consistent gets
          physical reads
          redo size
        515  bytes sent via SQL*Net to client
        465  bytes received via SQL*Net from client
          SQL*Net roundtrips to/from client
          sorts (memory)
          sorts (disk)
          rows processed

      可以看到select count(0)只需要23个逻辑读。一共10000条数据,10000/15=666.667 ,好,667+23=690!和第1个SQL的consistent gets竟然惊人的一致!这不是巧合,这就是consistent gets的计算公式。
我们还可以发现select count(0)和第2个SQL的consistent gets竟然也惊人地一致,都是23!TOM的解释是:
在select * from test order by 1;时,Oracle也把arraysize临时设为test表的行数,它把所有数据先全部取出来放到sort区做排序,而在sort区的读取就不算在consistent gets里了。所以虽然第2个SQL和select count(0)的consistent gets相同,但它的效率一定比select count(0)低,我们看执行计划里的COST便可以得知,第2个SQL的COST为7,select count(0)的COST为6,第1个SQL的COST也为6。(COST相同并不代表执行效率完全相同)
好了,现在明白了吧,这第二个原因就是我的实验现象产生的原因!