eygle.com   eygle.com
eygle.com eygle
eygle.com  
 

« 《深入浅出Oracle》排行榜再进一步 | Blog首页 | 传华友世纪下周将精简部门并裁员30% »

关于新书中Dirty Buffer的问答
modb.pro

今天有朋友在ITPUB上提问,问到新书中提及的一个说法:
P165页:一个buffer要么在LRU上要么在Dirty list,不能同时在多个list上。

问题是这样的:
如果一个block被移到Dirty list(=checkpoint queue?),Oracle怎么判断这个block到底是hot还是cold?

这个问题我是这样回答的:
所有Dirty Buffer,首先要被移动到Dirty List上去,然后从Dirty List上被写出。在移动到Dirty List之后,原有的计数被取消。也就是不存在Hot/Cold之说了,Hot/Cold是针对LRU List,并非Dirty List。

实际上,在内存中,可以反复对一个数据块进行多次修改,这可以变现在,在一个会话内多次修改一条记录,或者在某个会话提交之后,被其他会话再次修改。

但是这些修改会导致数据块在不同链表上的迁移,
如果一个数据块变得Dirty,它会在检查点队列上注册,也可能被移动到Dirty List上,如果在写出之前再次修改,那这个数据块可以被迁移出dirty list,继续被再次修改,但是数据块在Checkpoint Queue上的顺序不会改变,这是Oracle 8版本引入的变化,也正是由于checkpoint queue来确保写出的顺序才得以实现了增量检查点。


Biti也就此问题作出了回答,具体可以参考ITPUB的链接。

历史上的今天...
    >> 2019-08-15文章:
    >> 2014-08-15文章:
    >> 2013-08-15文章:
    >> 2007-08-15文章:
    >> 2005-08-15文章:
           孟静其人

By eygle on 2006-08-15 22:00 | Comments (14) | FAQ | 869 |

14 Comments

确切的说,当data buffer被移到lruw(注意是lruw而不是ckpt queue)后它的touch count将被清0,它将被添加到lruw的tail,而dbwr写出dirty buffer将从lruw的head开始(当然当checkpoint发生dbwr安装ckpt queue的顺序写出,lruw和ckpt queue分别针对不同的情况写出,ckpt queue负责checkpoint时候的写出顺序,而lruw负责其他dbwr写条件发生时写出的顺序,两者在dirty buffer write out时都起作用),当buffer存在lruw时,它的touch count是不起作用的,当它还没被write out时又被某个进程访问,那么它将被放入AUXILIARY lru list,同时它的touch count将重新开始起作用。

下面是一个例子

更新一个表,并设法使它的block buffer进入lruw
注意第一个地址0x21ff50ac,在这时候它在lruw,并且tch为0
MAIN WRT_LST Queue header (PREV_DIRECTION)[227e6320,21ff5100]
0x21ff50ac=>0x20ff23fc=>0x20feae6c=>0x20ff439c=>0x217eb3ec=>0x21bfab6c=>0x20ff03ac=>0x20fea4cc

CHAIN: 473 LOC: 0x27d510a8 HEAD: [21ff50ac,21ff50ac]
BH (0x21ff50ac) file#: 4 rdba: 0x0100f7c7 (4/63431) class: 1 ba: 0x21ebc000
set: 3 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0
dbwrid: 0 obj: 80582 objn: 80582 tsn: 4 afn: 4
hash: [27d510a8,27d510a8] lru: [20ff2450,21fee250]
lru-flags:
ckptq: [217f2584,20feae94] fileq: [217f258c,20feae9c] objq: [20ff24a4,223f73b4]
st: XCURRENT md: NULL tch: 0
flags: buffer_dirty only_sequential_access redo_since_read
LRBA: [0x218.1cd5.0] HSCN: [0x5.e85ddeec] HSUB: [1]
buffer tsn: 4 rdba: 0x0100f7c7 (4/63431)
scn: 0x0005.e85ddeec seq: 0x01 flg: 0x00 tail: 0xdeec0601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1


再次访问这个block

update test set object_id=object_id+1 where rowid=DBMS_ROWID.ROWID_CREATE(1,80582,4,63431,0);
update test set object_id=object_id+1 where rowid=DBMS_ROWID.ROWID_CREATE(1,80582,4,63431,0);
......
update test set object_id=object_id+1 where rowid=DBMS_ROWID.ROWID_CREATE(1,80582,4,63431,0);

它被移回AUXILIARY LRU LIST,并且tch开始重新计算

AUXILIARY RPL_LST Queue header (NEXT_DIRECTION)[20bf5260,20fee0f0]
......
0x21ffb3ac=>0x21fe679c=>0x21bf838c=>0x20bf6b7c=>0x20ffaccc=>0x20fec1ac=>0x21fee1fc=>0x21ff50ac(这个地址)
......
CHAIN: 473 LOC: 0x27d510a8 HEAD: [21ff50ac,21ff50ac]
BH (0x21ff50ac) file#: 4 rdba: 0x0100f7c7 (4/63431) class: 1 ba: 0x21ebc000
set: 3 blksize: 8192 bsi: 0 set-flg: 0 pwbcnt: 0
dbwrid: 0 obj: 80582 objn: 80582 tsn: 4 afn: 4
hash: [27d510a8,27d510a8] lru: [20ff2450,21fee250]
lru-flags: on_auxiliary_list
ckptq: [27dae774,223e8554] fileq: [27dae7c4,27dae7c4] objq: [2544db18,2544db18]
st: XCURRENT md: NULL tch: 5
flags: buffer_dirty gotten_in_current_mode block_written_once
redo_since_read
LRBA: [0x218.2862.0] HSCN: [0x5.e85de53c] HSUB: [2]
buffer tsn: 4 rdba: 0x0100f7c7 (4/63431)
scn: 0x0005.e85de53c seq: 0x01 flg: 0x00 tail: 0xe53c0601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data

你这个例子构造的比较特殊,其实就相当于一个session对一个Buffer进行了多次修改。

例子没写全,开始更新test表导致buffer进lruw是在其他session做的,进lruw后然后再开其他session再次访问这个buffer。

The LRU-W (write) list is used to hold buffers that aged out of the LRU but need to be written to disk before they can be reused.

这里说的不能重用是针对buffer被write to disk后变成free buffer来说的,在被写出磁盘前这个buffer是不能被覆盖的,不是指的是它不能被移回lru并被其他 session修改。

最后这个解释是全面的:D

Reuse是指被别的数据使用,对于当前Block是可以被继续pin或修改的。

对于你的这个测试,其实数据库就是省略了一个写回Disk,再从Disk读出来的过程。

直接在Buffer内,从lru-w到lru倒了一下手而已。

:)

因为lruw和lru无非是双向链表,保存的无非是buffer address,操作非常迅速,比发生io代价小得多,所以oracle还是实现的聪明的,呵呵

这个还是容易理解的,不过关于细节以前的确没有想得这么清楚。

谢谢wanghai同学:)

如今这么认真的人不多了 :)

我也给一个物理上的解释:是以前写的东西的一部分

基础的概念:
Hash buckets是一个结构,维护这数据缓存头的列表,是有相关的dba和类号来分类;
Hash chain是在一个hash bucket上的一个数据缓存头的列表。
LRU:是一种机制,用于确定在搜索空闲的缓存的时候,哪一个缓存能够被使用。


缺省的hash bucket数目是:DB_BLOCK_BUFFERS*2
通过设置参数_DB_BLOCK_HASH_BUCKETS能够重写hash bucket数目
每一个HASH BUCKET包含这一个缓存头的一个链,
每一个hash bucket有一个cache buffers chains latch来保护对这个链的同步访问。

这几个概念说这相对比较复杂,解释一下,这里还有一个缓存内存区域,这个是存放数据的物理位置,在上面有几个结构:
Hash bucket
Buffer header

其中Hash bucket的数目是通过参数设置的,之所以使用这个概念,主要是才查找缓存的数据的时候能够更快找到所需要的数据的地址。而在Hash bucket这个结构中有一个链,这个链是有Buffer header组成,在Buffer header这个结构上有指向存放数据具体位置的指针地址。在查找数据的时候,数据库通过hash算法确定是有哪一个Hash bucket来存放所需要的Buffer header,在通过Buffer header上的buffer address来确定具体数据存放的位置。

还有两个链,LRU和LRUW,这两个链也是通过Buffer header上的LRU chain的指针来实现的,每一个Buffer header只有一个LRU chain指针,这也就是为什么同一个块不能同时存放在LRU和LRUW的原因了。

在这里我们可以看到,这里组成了一个二维的一个网状的一个链网,这个联网相互交叉,在LRU和LRUW其实只是一个虚拟的一个链,是有Buffer header来实现,Hash bucket目的是实现数据地址的快速查找,但是LRU和LRUW是找到相应的各自的功能,Oracle就是通过这样的设计来实现一种物理结构实现两种不同的功能。

楼上的兄弟,写的真是详细,我最近也在研究这个缓存机制,希望能与你交流。能不能把你以前写的这个完整文章发给我一份呢,谢谢您了。我的信箱是ximengzhan@etang.com

eygle老师你好,请问AUXILIARY WRT_LST 的作用是什么?

我想问一下,当一个dirty位于ckpt queue的head的时候,如果这个块再修改但还没有完成或者说正在修改,这时候当checkpoint发生dbwr,dbwr需要写,这个dirty是写还出是不写出到磁盘呢?


CopyRight © 2004~2020 云和恩墨,成就未来!, All rights reserved.
数据恢复·紧急救援·性能优化 云和恩墨 24x7 热线电话:400-600-8755 业务咨询:010-59007017-7040 or 7037 业务合作: marketing@enmotech.com