HelloDBA [English]
搜索Internet 搜索 HelloDBABA
  Oracle技术站。email: fuyuncat@gmail.com  MSN: fuyuncat@hotmail.com   acoug  acoug 

一致性读(Consistent Reads)与buffer cache

[English]

作者: fuyuncat

来源: www.HelloDBA.com

日期: 2009-06-18 15:01:07

分享到  新浪微博 腾讯微博 人人网 i贴吧 开心网 豆瓣 淘宝 推特 Facebook GMail Blogger Orkut Google Bookmarks

在一致性读(Consistent Read)过程中,Oracle根据SCNundo segment/buffer中读取脏数据块的undo数据来保证查询数据的一致性。当查询读取了CR数据块时,为了提高后续CR的性能,会将CRcopybuffer中,后续的CR就直接读取buffer

在分析CR buffer之前,先简单看下buffer cache是怎么管理的。

我们知道,buffer cache的主要目的就是缓存那些被访问过的数据,以提高下次对这些数据的访问性能。由于一个数据库的被访问的数据量是很庞大的,但是buffer cache资源是有限的,这就需要一个对buffer cache的管理算法,以大大提高buffer cache的利用率。这一算法就是LRU算法,其基本思想就是让那些经常被访问的数据能尽量长时间的保留在buffer中,以提高数据库的整体性能。

Oracle通过两个链表来实现这种算法的管理:LRU ListWrite List(也叫Dirty List)。在LRU List中,链接的是所有空闲块(Free Buffer)、正在被使用的块(Pinned Buffer)以及所有还未被放到Write List中去的脏块(Dirty Buffer)。当一个数据块被访问到了时,就会被放到LRU链表的MRUthe Most Recently Used)端,这样,那些很少被访问到的数据块就会逐渐移动到了链表的LRUthe Least Recently Used)端。当需要访问一个数据块时,用户进程会先搜索(通过hashLRU List,看该数据块是否已经被cache住,如果有,就直接使用(buffer hit),如果没有(Buffer Miss),服务进程会从LRU ListLRU端开始搜索空闲块,并且在搜索过程中,将找到的脏块都转移到Write List上去。如果搜索一定数量(一个内部的Threshold值)的buffer块还没有找到空闲块时,服务进程就会发信号给DBW0进程将脏数据块写入磁盘并释放。

回过来再说CRCR是发生在多个事务对相同数据进行读写时,为了保证读进程不因为时间差(query消耗的时间)而造成数据差异,让读进程读取到与本身时间戳(SCN)相符的数据块镜像。在发生CR时,CR块也会被cachebuffer中,以提高query后续的对该数据块的一致性读的效率,此时的CR buffer块会打上相应querySCN标志,该数据块也只能被这个query使用。那么,具体什么样的情况会发生CR呢?我们通过代码演示来分析发生CR的各种情况。

注:下面是测试表t_cr的创建脚本:

SQL> create table t_cr as select * from user_objects;
 
表已创建。
 
SQL> alter table t_cr add constraint r_cr_pk primary key (object_id);
 
表已更改。

注:下面查询中objd是表对象的data_object_id,不是object_id,该值可以从dba_objects中查到。

 

一、什么情况下发生CR

 

·         在“读”事务开始时,数据块已经被其他事务修改但未被提交,但在数据块被读取到之前,修改已经被提交:

 

时间:

T1

T2

T3

T4

A事务

Block Updated

 

Commit

 

C事务

 

Query begin

 

Read Block(Consistent Read)

 

B:
SQL> alter system flush buffer_cache;
 
系统已更改。
 
SQL> select objd,file#,block#,status,dirty from v$bh where objd=186467 and block#= 147146 and status !='free';
 
未选定行

 

A事务更新数据,未提交:

A:
SQL> update sys.t_cr set object_name = 'NB' where object_id=20;
 
已更新 1 行。

 

修改过的数据块被cache到了buffer中:

B:
SQL> select objd,file#,block#,status,dirty from v$bh where objd=186467 and block#= 147146 and status !='free';
 
      OBJD      FILE#     BLOCK# STATUS         DI
---------- ---------- ---------- -------------- --
    186467          1     147146 xcur           Y

 

C事务开始queryquery时间较长,运行中且未读取到被A事务修改过的数据块:

C:
SQL> select to_char(count(1)) from user_objects, user_tables
  2  union all
  3  select object_name from sys.t_cr where object_id=20;
 
(Query运行中......)

 

A事务提交修改

A:
SQL> commit;
 
提交完成。

 

这时,C事务读取到该数据块,对数据块的读取是一致性读(CR),其读取的数据是修改前的数据:

C:
(Query完成)
TO_CHAR(COUNT(1))
-----------------------------------------------------------------------
159390
AAA

 

看到UNDO数据被copy到了buffer cache中作为CR buffer存在:

B:
SQL> select objd,file#,block#,status,dirty from v$bh where objd=186467 and block#= 147146 and status !='free';
 
      OBJD      FILE#     BLOCK# STATUS         DI
---------- ---------- ---------- -------------- --
    186467          1     147146 xcur           Y
    186467          1     147146 cr             N

 

再次读取就是修改后的数据:

C:
SQL> select object_name from sys.t_cr where object_id=20;
 
OBJECT_NAME
-----------------------------------------------------------
NB

 

·         “读”事务开始之前,数据块被其他事务修改,当“读”事务读取到该数据块时,修改仍未提交,发生一致性读

时间:

T1

T2

T3

T4

A事务

Block Updated

 

 

Commit

C事务

 

Query begin

Read Block(Consistent Read)

 

 

B:
SQL> alter system flush buffer_cache;
 
系统已更改。

 

A事务修改数据、未提交:

A:
SQL> update sys.t_cr set object_name = 'AAA' where object_id=20;
 
已更新 1 行。

 

C事务读取数据块,发生一致性读

C:
SQL> select object_name from sys.t_cr where object_id=20;
 
OBJECT_NAME
--------------------------------------------------------------
NB

 

CR块被cache

B:
SQL> select objd,file#,block#,status,dirty from v$bh where objd=186467 and block#= 147146 and status !='free';
 
      OBJD      FILE#     BLOCK# STATUS         DI
---------- ---------- ---------- -------------- --
    186467          1     147146 xcur           Y
    186467          1     147146 cr             N

 

以上这两种情况可以视为同一种情况:

当数据块在某个事务中被修改了,所有开始于“修改”事务开始后、提交前的所有会读取到该数据块的“读”事务,在读取到该数据块时都会发生一致性读。

 

·         “读”事务开始后、在读取数据块之前,数据块被其他事务修改且未提交,当读取到该数据块时仍未提交:

时间:

T1

T2

T3

T4

A事务

 

Block Updated

 

Commit

C事务

Query begin

 

Read Block(Consistent Read)

 

 

B:
SQL> alter system flush buffer_cache;
 
系统已更改。

 

C事务开始queryquery时间较长,运行中且未读取到被即将被A事务修改的数据块:

C:
SQL> select to_char(count(1)) from user_objects, user_tables
  2  union all
  3  select object_name from sys.t_cr where object_id=20;
 
(Query运行中......)

 

A事务修改数据块,未提交

A:
SQL> update sys.t_cr set o