当用户发出DML语句,请求更新某些表里的数据时,在日志缓冲区中生成重做记录的过程如下所示。
<!--[if !supportLists]-->1) <!--[endif]-->服务器进程判断buffer cache中是否存在要求被更新的数据块,如果没有,则从数据文件中将数据块调入buffer cache。然后以排他(Exclusive)模式将该数据块钉住(ping)。注意,这里的数据块包括回滚段段头(事务表)、回滚段数据块以及表所对应的数据块等。
<!--[if !supportLists]-->2) <!--[endif]-->服务器进程构造一组改动向量(change vector)来描述对数据块所做的变化过程。这组改动向量会放在session的PGA中。实际上,这组改动向量也就是重做记录了。
<!--[if !supportLists]-->3) <!--[endif]-->根据重做记录的大小,判断在日志缓冲区中需要多少空间。
<!--[if !supportLists]-->4) <!--[endif]-->判断当前的SCN值,并将其存放到重做记录中。注意,并不是每个重做记录都具有不同的SCN值,不同的重做记录可能会共享相同的SCN值。
<!--[if !supportLists]-->5) <!--[endif]-->进程获得名为redo copy的latch。
<!--[if !supportLists]-->6) <!--[endif]-->进程获得名为redo allocation的latch。
<!--[if !supportLists]-->7) <!--[endif]-->检查当前是否有其他进程生成了比当前持有的SCN更高的SCN值。如果是,则生成一个新的SCN值,并代替第四步所持有的SCN值。
<!--[if !supportLists]-->8) <!--[endif]-->判断是否有足够的空间容纳当前的重做记录,这里足够的空间既包括日志缓冲区,也包括联机日志文件。这时的逻辑比较复杂。
<!--[if !supportLists]-->a) <!--[endif]-->如果联机日志文件中有足够的空间,则判断日志缓冲区是否有足够的空间。
<!--[if !supportLists]--> I. <!--[endif]-->如果日志缓冲区中没有足够的空间,则进程释放redo copy latch和redo allocation latch。然后在下面的两个选项中做一个选择:(1)如果这时已经有其他进程或其他条件触发了LGWR,则等待LGWR的完成;(2)如果这时LGWR没有启动,则触发LGWR启动。为了防止多个进程同时触发LGWR,oracle还引入了名为redo writing的latch。当LGWR进程在写重做记录到联机日志文件的过程中,会一直持有redo writing latch,这时任何试图获得该latch的进程都必须等待。实际上,当进程发现没有足够的空间以后,会立即尝试获取redo writing latch。如果不能获得该latch则说明LGWR正在清空日志缓冲区,所以进程会等待,过一段时间再次尝试去获得该latch;如果获得了该latch,则立即检查这时日志缓冲区中是否还有足够的空间,如果有,则释放redo writing latch,并再次尝试获得redo copy latch和redo allocation latch;如果没有,则释放redo writing latch,同时触发LGWR,这时LGWR获得redo writing latch。
<!--[if !supportLists]--> II. <!--[endif]-->如果日志缓冲区中有足够的空间,则从日志缓冲区中分配所需大小的空间,然后释放redo allocation latch,注意这时仍持有redo copy latch。将改动向量拷贝到所分配的日志缓冲区中,将重做记录所对应的脏数据块挂到检查点队列(checkpoint queue)上去。最后,释放redo copy latch。
<!--[if !supportLists]-->b) <!--[endif]-->如果联机日志没有足够的空间(可能有一部分可用空间,但是不够用。实际这就是有时你会看到归档日志文件的尺寸会小于联机日志文件的尺寸的原因),则进程会检查是否已经有进程已经触发日志切换了,如果有,则当前进程等待,如果没有则触发日志切换。然后,重复第8步。
<!--[if !supportLists]-->9) <!--[endif]-->当在日志缓冲区中写完重做记录以后,检查当前日志缓冲区中的重做记录的数量是否达到限定值,如果是,则必须触发LGWR进程。
<!--[if !supportLists]-->10) <!--[endif]-->最后,进程更新buffer cache中的数据块。
我们可以用一个图来描述一下上面所说的日志缓冲区的管理过程,如下图四所示。
图四
上面详细介绍了重做记录的生成过程,现在详细介绍一下LGWR写重做记录的过程。
当LGWR进程启动时,
<!--[if !supportLists]-->1) <!--[endif]-->首先会尝试获取redo writing latch,以确保其他前台进程不会继续触发LGWR进程。
<!--[if !supportLists]-->2) <!--[endif]-->然后会获取redo allocation latch,这是为了防止前提进程继续分配更多的日志缓冲区,否则日志缓冲区中的待写入日志文件的日志块不断增长,LGWR是无法确定到底应该写多少日志块的。
<!--[if !supportLists]-->3) <!--[endif]-->在获得了redo allocation latch以后,LGWR开始确定应该写哪些日志块到日志文件。从上次LGWR启动以来所写的最后一个日志块到这个时间点时的最后一个被使用的日志块,这段范围内的日志块都是要被写入日志文件的,其中既包含写满的日志块,也可能包含还没有写满的日志块。特别注意,这个时候,可能还有前台进程正在向这段范围中的日志块拷贝重做记录。因为LGWR启动时,并不会阻碍前台进程获得redo copy latch,也就不会阻碍前台进程拷贝重做记录了。这样的话,LGWR就不会阻碍前台进程向日志缓冲区的其他可用的日志块中拷贝重做记录了。
<!--[if !supportLists]-->4) <!--[endif]-->在确定了要写哪些日志块以后,生成一个新的SCN号。
<!--[if !supportLists]-->5) <!--[endif]-->LGWR释放redo allocation latch,并释放redo writing latch。
<!--[if !supportLists]-->6) <!--[endif]-->LGWR会等待前台进程完成对LGWR所要写入文件的日志块的更新操作。这是通过判断这些待写的日志块上的redo copy latch是否都被释放来决定的。
<!--[if !supportLists]-->7) <!--[endif]-->LGWR将第4步生成的SCN号拷贝进待写的日志块的块头里,触发物理写操作,将这些待写日志块写入联机日志文件里去。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/751371/viewspace-567588/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/751371/viewspace-567588/