Oracle 实例崩溃恢复原理剖析 -- 检查点队列的作用与意义

这篇文章是参考甲骨论老相老师的教学视频

http://v.youku.com/v_show/id_XNDAxMzI3NzI4.html

所做的学习笔记



检查点队列的内容已经在上一篇文章里讲过:

http://blog.csdn.net/nvd11/article/details/8749393

这篇章虽然是讲实例崩溃恢复的原理, 但实际上讲的就是checkpoint queue的作用与意义啊.



1. Oracle 实例崩溃后, 内存里的修改后数据并没完全保存入硬盘


Oracle修改数据, 是在Data buffer cache里修改的,  一旦1个buffer被修改, 就成为了脏buffer, 所谓脏buffer意思就是指这个buffer跟磁盘里的对应block的数据不同了.   oracle的后台进程DBWR 会不断地将Data buffer cache里的脏buffer 写入硬盘.


但是当数据库突然崩溃的时候,  Datab buffer cache很可能还存在一些未写入硬盘的脏数据. 

就出现数据丢失.



2. 数据丢失也分两种情况.     

          2.1  可以丢失的数据

           意思就是这些数据丢失了没关系, 不影响数据一致性.

           那么有什么数据是可以丢失呢,  之前我们提过,  Oracle关于数据一致性有一个很重要的原则:

                    Oralce会保证用户已经commit 的改动不会丢失.        

           言下之意就是未提交的改动就允许丢失了,  其实逻辑上也是的,  就例如用户编辑1个execl文档,  但是并未保存, 这时突然windows死机..    你往excel文档作的改动就没了,  哎呀弄得我好像在黑微软似的...  那把windows死机改成突然断电啦.

 

            总之就是Databuffer中未提交的脏buffer, 是允许丢失的.


         2.2 不能丢失的数据

            这个很明显啦,  就是已经commit, 但未被DBWR 写入磁盘的脏buffer理论上是不允许丢失的, Oracle会有机制恢复这些脏buffer



3. 所以oracle利用日志找回一些脏buffer

          这些脏buffer就是指上面已经commit过的脏buffer啊.

          通过什么来找回来呢,  是同过修改日志啊.  

           那么修改日志在哪里呢?


            我们知道,  但server process一但修改了1个buffer, 这个buffer 就脏了, 然后server process会同时将对应的改动日志写入log buffer.

             而log buffer 既然是缓存, 肯定就是位于内存中的啦,  所以后台进程LGWR会不断将log buffer里的日志写入log 文件(硬盘)


           所以脏块的修改日志的位置有两个地方, 第1个就是在log buffer里,  第2个就在log files里.


            我们之前也提过,  用户一但commit 1条transaction , 那么对应这个transaction的修改日志就会被写入到log files.

            也就是说只有当oracle把对应日志从Log buffer 写入到 log files完成时, oracle才会提示用户commit 完成!


             那么当oracle 突然崩溃后,  log buffer里的日志全部就没了,  不过既然这些丢失日志是在log buffer 里的. 也代表这些日志对应的改动是未提交的,  也就是logbuffer 里的日志丢失也没关系,  没提交的改动嘛

 

              那么已经提交的改动, 日志都在log files里, 也就是重后还能找回来的, 既然日志能找回来, 所以理论上那些已经提交的脏buffer就能通过磁盘block 和日志重新构造出来了.


4. Oracle 必须知道logfiles里对应数据未写入硬盘的日志的位置.

                当oracle 检测出上次非正常关闭时,  就会执行数据恢复动作.


                 那么恢复的目标是什么呢, 就是恢复到数据库崩溃那个瞬间的状态...

                 就会吧崩溃那个瞬间的已提交的脏buffer 都恢复出来,    那么oracle就会去查找redo log files.(log buffer里的日志是未提交的, 就忽略了). 

            

                  好了, 但是redo log files里的日志很多啊, 都把他们对应改动恢复吗?

                  当然不是了, oracle 只会恢复那些未被DBWR 写入的脏buffer(写入里就变成干净buffer了),  实际上redo log files绝大部分日志对应的改动都已经被写入到data files.  所以oracle 就会从redo log files找出一段 对应数据未被DBWR写入磁盘的日志.


                 那么这段日志在redo log file的哪里呢?  

                 我们也提过oracle 是严格按照日志产生的时间(数据改动的时间) 未顺序去写入 redo log files的,  所以这段日志肯定就是log files里最新写入的那一段了, 就是在日志文件的尾部啊.


                好了, 所以到这里,  这段日志的终点可以决定了, 就是log files 的最新的一条日志,    它的位置就是上文提到的on disk RBA啊.   

                但是起点在哪里呢?


5. 可以通过 Checkpoint queue 找到最早未被写入硬盘的脏块buffer的日志位置.

               回顾一下, checkpoint queue里的内容:

                 checkpoint queue 是1个队列,  是以脏buffer的第一次被修改(变脏的时间) 为顺序, 实际也是以脏buffer的LRBA地址(首次改动的日志地址)的一条脏buffer链.

                 可以理解出:

                 1. 数据崩溃瞬间所有脏buffer都在 check point queue 上

                 2.checkpoint queue的第1个脏buffer的首次变脏时间 在 数据库崩溃瞬间里面所有脏buffer里的首次变脏时间里是最早的.

                     也就是说checkpoint queue排第一的那个buffer的LRBA 是整个数据库中关于脏buffer的日志中最早的, 也是在日志文件中排的最前的.   这个LRBA 也是整个checkpoint queue 的LRBA


                   


                    如上图, 整个checkpoint queue的LRBA 会比 on disk RBA早,   而on disk RBA 就是最新被写入硬盘的那条日志地址, 所以 LRBA那个日志就肯定位于磁盘中了.


                    那么整个checkpoint queue LRBA 前面那些日志是什么呢,  首先那些地址比 checkpoint queue 的LRBA对应日志产生时间要早.  

                    假如其中1个日志A,   他对应的buffer 是 Buffer A,   如果这个日志A的地址在日志文件里比checkpoint queue  靠前, 那么这个Buffer A 当前肯定是1个干净buffer了.

                            怎么理解,  假如这个Buffer A是脏的,  那么肯定被挂在checkpoint queue 中, 而Buffer A的LRBA 的地址肯定早于或等于日志A,  所以也肯定早于 checkpoint queue 的LRBA才对,  所以check point queue的LRBA应该是Buffer A的LRBA啊, 所以就矛盾了, 由此可以反证出 checkpoint queue LRBA 前面的地址都是干净buffer的日志.


                    如下图:



                      所以我们想找的恢复日志的起点就是 checkpoint queue的最早buffer 的LRBA的地址啊,  就如上图 dirty buffer1 的日志1 的地址,     终点就是on disk RBA了, 所以数据库恢复需要用到的日志就是 上图 两条虚线之间的日志啊

                     当然, 我们说Logbuffer里面的日志丢失就丢失了, 因为他们对应的改动都是未commit 的,   但是logfiles里面的( on disk RBA 与 checkpoint queue LRBA)之间的地址都是已经commit的吗?

                     这个就不一定了, 因为oracle 的LGWR每3秒会把 log buffer 里的数据写入log file 一次, 所以这断区间的日志对应的buffer都有可能是未commit的.  而且还有可能是干净的, 例如上图的dirty buffer2,  假如DBWR 将dirty Buffer 2写入db files,  那么dirty buffer2就是一个干净buffer了.  而且它会被移除checkpoint queue(待考证, 因为队列是只能从头部移出, 尾部加入的)  但是它的改动日志还是会处于 我们要恢复日志的区间啊.


        

6. oracle 崩溃中恢复时怎么找到checkpoint queue 的LRBA.


                    上面我们已经知道checkpoint queue 的LRBA地址的重要性了,  它是用于恢复数据库崩溃前脏buffer的起始日志位置啊.

                    但是一旦数据库崩溃, checkpoint queue本身就不存在了, (checkpoint queue 毕竟是一条由内存buffer组成的队列, 如果buffer都没了, checkpoint queue也不会存在)   那么如何找到它的LRBA呢.


                    这个就要参考上文中地提到的一点:

                    增量检查点(incremental checkpoint):
                  
这时CKPT 会将  check point queue第一个脏buffer 的 LRBA地址 记录到控制文件中.


                   这个事件每3秒一次, 也就是说每3秒CKPT 进程会更新一次控制文件的 checkpoint queu LRBA地址.

                   所以oracle 崩溃只需要从控制文件找出 checkpoint queue的LRBA 就可以确定Logfiles中的对应日志起点啦.


                   也需要有人会问,  既然增量检查点每3秒发生一次,  代表了崩溃后的控制文件的 checkpoint queue LRBA可能不是实时的, 会有问题吗?


                    我们再分析一下,  假如某个1个增量检查点  checkpoint queue 的LRBA 是 A,   这时CKPT 把A 写入了控制文件,  好了, 这时过了1秒,  checkpoint queue 里的LRBA 变成了B,  再过1秒,  这时服务器崩溃了, 因为下1个增量检查点还没发生, 所以控制文件里的LRBA 还是A,     而当时checkpoint queue 的实时LRBA 是B,  这样会有问题吗>


                   这就要搞清楚 checkpoint queue 的内容了,  首先 checkpoint queue的 LRBA 是 checkpoint queue头部第一个脏buffer  A的LRBA,  如果这时  这个LRBA更新了, 也就是代表头部的脏buffer 变了,  这时只有1种情况, 就是原来的LRBA 对应的Buffer A被移出了 checkpoint queue, 为什么会被移除了, 只能是这个buffer A变干净了啊. 


                    返回刚才的情况, 虽然控制文件里的LRBA 不是 事实上的B, 已是之前的LRBA A,  但是B的地址肯定会比A的迟,  后果恢复时把干净的Buffer A 也恢复出来了, 这个完全无伤大雅啊.

      

                    只不过为了避免恢复出太多多余的干净buffer,  所以才将增量检查点设成每3秒一次啊.


7. oracle 崩溃中恢复时会根据日志恢复出已经commit的脏buffer 和未commit 的脏buffer.

                     在本文第5点提过了,  oracle用于数据恢复的日志中, 肯定包含了全部已经commit 脏buffer 的日志,  但是还会有一些未commit 脏buffer的日志.

                     这样的后果就是恢复时把所有已经commit的脏buffer 全部恢复出来了,  但是也会把一部分未提交的脏buffer 也恢复出来.


                      虽然恢复了一些多余的脏buffer, 但毕竟保证了commit过的脏buffer没有丢失啊!  所以还是能保持数据一致性的原则的.

                  

                      而对于那些被恢复出来的多余的未提交的脏buffer, 为什么说是多余的呢, 因为修改它么的sessions都不存在了, 所以oracle 会将它们全部rollback ,  返回干净buffer的状态.



8. 假如没有check point queue , 会有神马后果?

                  到这里我们大概清楚checkpoint queue 是用来确定数据库崩溃时,  跑日志的日志起点的

                 假如没有这个check point queue , 那么oracle就确定不了数据库崩溃前1 ~ 3秒的LRBA的起点,  所有只能把"所有"的日志用来跑一遍了.

                 日志跑多了怎么办?  例如 最旧日志是从10月1号保存的,  oracle10月15号崩溃,  10月16号重启恢复, 如果把所有日志都跑一遍, 就跑到了10月1号的状态了??

                 问题也不大, 因为日志还存在嘛, 只需要把10月1号前滚到10月5号就ok啦.

                 

                  但是这段恢复时间则很慢长了, oracle 8i 之前就是没有checkpoint queue的, 所以一旦崩溃, 下次开机恢复就很慢了.

                  checkpoint queue的根本意义就是用来加快oracle 崩溃后恢复的速度啊!




最后总结下 oracle数据库崩溃恢复到底做了什么:

前提: oracle 里一但1个 transaction 被 commit, 那么对应这个transaction的所有改动日志都会从logbuffer 写入log files(如果未写入话)

1. oracle 启动时自检发行上次不正常关闭

2. oracle 从控制文件找出最新的 checkpoint queue LRBA地址(运行时每3秒一次增量检查点, 更新1次)

3. oracle会根据 起点为 上面的 LRBA  终点为on disk RBA( 硬盘上最后1个日志地址) 这段区间内的日志, 把对应的buffer恢复出来( 也叫前滚 roll foward)

4. 目地是恢复出所有已经commited 的所有脏buffer,  对于那些被恢复出来的未commited 的脏buffer,  oracle 会将它们回滚.



                  








                    



















d

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值