31、buffer_cache_1(CBC链、LRU链、LRUW链、检查点)

buffer cache


图解:
1、buffer cache
2、数据文件
3、redo log
buffer cache被格式化为很多的块,datafile也被格式化很多块,redo log(有三个日志文件,日志文件被格式化为一个一个的块,这个块叫做:扇区块(512字节),这个块的地址叫:RBA(redo block adress))

buffer cache里的几个概念:

图解:
假设,SP要访问某个数据行,发现这个数据行在某个block里面,知道这个block的地址dba;
现在有一个需求:server process根据这个块的地址去访问一个块;首先要在buffer cache里面找,这就需要链了,在buffer cache里面以块地址的方式链起来;然后将datafile中的块读到buffer中,会将块的地址进行哈希,然后求模,挂到buffer cache某一个链的某个位置;
SP根据这个块的地址去找一个块的时候,会先将获取到的块地址进行哈希,求模,如果这个块在buffer cache里面有的话,就获取并持有这个链所对应的latch,然后遍历,找到这个块的时候,释放latch,读buffer块的瞬间加上pin锁;

1、CBC链(CBC找buffer的过程):为server process服务的
现在,假设buffer cache有100W个块,server process根据地址来找buffer,不能遍历100W个去寻找,就需要以地址的方式生成链,然后在链上找,就有一种链:CBC链(chain buffer cache);然后还有CBC latch,来保护CBC链;
SP根据这个块的地址去找一个块的时候,会将获取到的块地址进行哈希,求模式,然后知道这个块在哪个链上,并持有这个链的latch,然后遍历,找到这个块的时候,释放latch,然后读或者写或者修改这个buffer,读或者写或者修改这个buffer的瞬间加上pin锁

2、buffer里面块的状态

图解:
buffer cache里面有很多的buffer,然后对应磁盘的block,buffer有几种状态:
1、干净块(clear buffer):
buffer里的一个块对应磁盘中的一个数据块,然后磁盘里面的block调到buffer里面之后从来没有被修改过,或者是修改之后写到了磁盘上,就是说,buffer里面的数据和磁盘block上的数据一致,这种buffer叫做:干净块(clear buffer)

2、空闲块(free buffer):
没有block数据写进去的buffer
clear buffer和free buffer,我们叫:可用buffer:对于free buffer,因为没有被使用,所以就可以直接使用;对于clear buffer,我们可以释放掉,然后把数据写进去

3、脏块(dirty buffer):
磁盘中的block数据调到buffer里,然后buffer里面的数据被修改了,buffer和磁盘中数据不一致,需要DBW将buffer里的数据写到磁盘中,但是在数据没有被写进磁盘的时候,这种buffer是不能覆盖使用的

4、pin buffer
就是说,sp要读或者写buffer的瞬间,会将buffer pin住,这个buffer就叫做pin buffer,然后其他sp就不能访问这个buffer

free buffer waiters(等待事件)
就是sp想把block调到buffer中去,sp需要在buffer中寻找可用块(干净块或者free buffer),这个可用块绝大多数是干净块;sp将覆盖干净块;但是刚覆盖完,另一个进程要读覆盖之前的buffer,数据库就要产生物理读;

所以我们希望是:sp在buffer里寻找可用块的时候,寻找的干净块是在很长的一段时间里没有sp访问的,就是希望这个干净块是不经常被使用的buffer,然后进行覆盖,这又涉及到了chain链;
这种链,挂的是可用块,然后这些可用块最好是有冷热区别(冷热是区分buffer是否是被经常访问的)的可用块

LRU链:为server process服务的
LRU链(last recent used):最近最少使用的
LRU链的特点是:挂的是可用块,然后按照冷热的顺序挂起来的

图解:
现在有六个buffer,需要有两个CBC链(按照地址链起来的),还需要一个LRU链(可用块,然后按照冷热的顺序链起来的);
将来根据地址找buffer的时候,根据CBC链找;然后要找冷的可用块的时候,找LRU链;这时候一个buffer可以在多个链上;

然后DB writer进程,1、需要找到buffer cache的脏块;2、需要将冷的脏块写到磁盘上;
buffer cache将脏块写到磁盘上的时候,其中一类脏块写到磁盘上,马上又变成脏块,这一类脏块叫:热的脏块;寻找热的脏块存在LRU链

LRUW链:为DBWR服务的
还有一类脏块写到磁盘上,buffer变成干净可用块,并且很长内没有再被使用,这一类脏块叫:冷的脏块;寻找冷的脏块存在LRUW链,根据LRUW链将冷脏块写到磁盘上;

不同的链有不同的作用

redo log的几种状态


图解:
目前正在使用redo log 1,这个redo log的状态为:current状态
redo log 2,没被使用,对应三个buffer,并且对应的脏块全部写到磁盘中去了,这个redo log的状态为:inactive
redo log 3,没被使用,对应四个buffer,但是存在脏块未被写到磁盘上,这个redo log的状态为:active;这个redo log是不能被覆盖的;
如果redo log 3被覆盖了,这时候数据库突然崩了,未被写到磁盘的脏块就丢失了;然而redo log被覆盖了,未被写到磁盘的脏块就无法写到磁盘上了

日志预写

LGWR(Logwrite)进程的工作:
1、每隔3秒钟,触发把log buffer写到入redo log里;
2、当log buffer达到1M,把log buffer写到入redo log里;
3、当log buffer达到1/3的时候,把log buffer写到入redo log里;
4、commit的时候,也会触发logwrite进程把log buffer写到入redo log里;

DBWR进程的工作:
1、每隔3秒钟,写一部分将脏块写到数据文件中
2、sp根据LRU查找可用块,找了半天没找到,数据库认为buffer中都是脏块,就会触发dbwr将脏块写到磁盘中
3、等等


图解:
一个SP修改一个buffer,产生的日志在redo log里面;然后这时候dbwriter想把buffer写到磁盘上,会先触发logwriter将log buffer写到磁盘的redo log里面,也就是保证buffer修改数据之后所对用的日志写到磁盘上,然后再将buffer写到磁盘上;保证先写日志,然后再写数据,我们把这叫做:日志预写
redo log始终保存最新的修改,redo log总比数据文件新;如果脏块写到磁盘,redo log一定写到磁盘中了;即使脏块没被写到磁盘,redo log也可以先写到磁盘中

检查点队列

oracle链还存在一种:检查点队列(其实就是一些脏块的链)也是一个链:
1、链的是脏块
2、按照脏块第一次被修改(最早脏)的时间链起来


图解:
在buffer cache里面的一些buffer,第一,通过CBC的方式链起来,它里面有一些脏块,另外的干净块通过LRU链链起来;然后脏块通过LRUW链按照冷热(就是按照最近修改的一个频率)的顺序链起来;


图解:
然后把五个脏块拿出来,还要用链链起来,按照最早脏(第一次被修改)的时间链起来,这个链叫做ckpt队列;

LRBA地址


图解:
现在五个脏块连成一串,然后最早脏的那个块对应的日志在第二组redo log里面,redo log分组,这有三组redo log,日志序列号越靠后的日志越新;现在第一组日志为:current,序列号为:100,第二组:99号,第三组:98号;然后最早脏的块的日志的下面的那些日志都比它新,在redo log里面是这条日志的上方的那些日志,然后最早脏的这个块的日志在redo log里面所对应的RBA地址,我们叫做:LRBA(low redo block address)地址;
每一个脏块的头部都有一个LRBA地址,就是第一次脏的时候在redo log里面的RBA地址,之所以要把脏块按照第一次脏的时间排序的目的:就是为了寻找最旧的块的LRBA地址;
检查点队列里面最早脏的块的LRBA就是检查点队列的LRBA,也就是整个数据库的LRBA;

找到最旧的块的LRBA地址的目的是:
假如数据库崩了,使用redo log,因为redo log已经写到磁盘上,但是脏块还没写到磁盘;数据库崩了,脏块没有写到磁盘,数据库重新起来以后,使用redo log重新构造出脏块;
如果日志还在log buffer还没写到redo log里面的,数据库的脏块就无法重新构建,因为日志还没写到redo log,说明事务还没有提交,数据库就不需要恢复,无所谓;保证所有已经提交的事务对应的脏块都恢复出来,回复的时候,redo log的起点是检查点队列对应的LRBA,终点是current状态的redo log的最后一条日志

检查点队列的作用:
通过检查点队列里面最早脏的块所对应的LRBA地址,找到在redo log里面的LRBA,也就是找到整个数据库的LRBA;然后将来数据库崩了以后,使用这个LRBA以后的日志,往这个LRBA的前面读日志,将脏块恢复出来

崩溃恢复

CKPT进程,oracle运行期间有一个检查点队列,oracle有一个文件叫做控制文件,在buffer cache中有一个ckpt队列,每3秒进程启动一次CKPT,将LRBA写到控制文件中,这个就叫做:增量检查点
然后DBWR将队列中的某些脏块写到磁盘上,完了之后,3秒钟以后,ckpt又启动起来了,又将新的最早脏的块的LRBA写到控制文件中去,两个进程互不干扰;
ckpt进程写完之后,数据库突然崩了,数据库重启启动之后,oracle会启动一个smonitor进程,smonitor进程发现数据非正常关闭,因为数据块的SCN号大于数据文件的SCN号,这时候就会读控制文件,记录着LRBA,然后读LRBA,然后找到对应redo log里面的日志,恢复出崩溃之前的脏块,这叫数据库前滚,崩溃恢复就完成了;
这时候数据库重新启动了,但是数据库中还有许多未提交事务,这些未提交事务所对应的数据块,当select的时候,读到这个块的时候,读这个块的对应的undo,根据事务表中的事务槽发现事务没提交,但是会话已经死了,这时候数据库server process就会将事务使用undo将它回滚。---不会主动回滚,只有读到这个块的时候,才会进行回滚

完全检查点
就是:这个检查点触发DBWR将所有脏块写到磁盘中
数据库正常运行期间,是不可能触发完全检查点的,除非是手动触发数据库正常关闭的时候,会触发完全检查点,将数据库的所有的脏块写到磁盘文件上

posted @ 2024-11-22 11:09  一只c小凶许  阅读(1)  评论(0编辑  收藏  举报