就假设我修改了一条数据:update people set name='Fusnow' where name='old fusnow';
那我需要做的事情包括:
在redo log buffer生成redo信息(包括对表的redo,undo的redo,索引什么的就不考虑了)
在buffer cache里修改name='Fusnow',修改undo segment
------------------
情况1
如果一切正常,我现在commit,commit会触发lgwr把redo log buffer里的信息写入到磁盘的redo log file,如果这个操作成功完成,那我的了,现在如果系统崩溃,尽管可能会丢失buffer cache里的脏数据,但可以从redo log里找到重做信息,所以,可以恢复,当然这个情况不用rollback。
------------------
情况2
如果lgwr把redo log buffer里的信息写入到磁盘的redo log file的过程中系统就崩了,那实际上对用户而言commit没有成功就报错了,这时候datafile和redo log上都没有我们要的信息,所以正好,系统启动的时候我这个改动的roll forward/back 都省了。
------------------
情况3
如果commit之前,有什么原因导致我们要flush buffer cache,比如buffer cache满了,oracle要吧buffer cache里的dirty data写入到磁盘,这要触发dbwr,但dbwr写之前一定要触发lgwr,先把redo log buffer里的数据写入到redo log file。原因很简单,如果不触发lgwr,由于对于我的这个改变的脏数据包括表的改动和undo改动,而dbwr很可能不是在一次io里面把这些脏数据写入磁盘,如果就是这么巧,我们先写入了表的改动,还没来得及写undo的脏数据,数据库崩了,那现在我们的状态是datafile里面有改过的表数据,没有undo数据,redo里面没有redo信息,由于我是没有commit,所以要回滚,但这种情况下是不可能的,因为undo丢了,可以重新生成undo的redo也丢了。因此,dbwr一定要触发lgwr。
如果在dbwr触发lgwr的前提下,我们先写入了表的改动,还没来得及写undo的脏数据的时候数据库崩了,我们实际上是先roll forward,通过redo生成undo,再rollback,通过undo回滚事务。
当然,有兄弟也提过了了,先后顺序是1.roll forward, 2.open database, 3.rollback,我想这主要是oracle为了节省时间,因为设计上按照1.roll forward, 2.rollback, 3.open database的顺序应该也是可以的,但这样比较慢,我们完全可以在roll forward结束后马上open database,然后让用户访问数据库的其他部分,让smon慢慢rollback,这时候如果用户想访问正被回滚的数据是会被堵塞的。当然,在fast模式下,oracle会优先rollback用户想访问的block,让用户尽快可以访问这些正被rollback的数据。
案例(2)
假定有一下操作语句:
update gaojf set name='exitgogo' where name='old_exitgogo';
这个语句是这样执行的:
1:首先检查name='old_exitgogo'是否记录在buffer cache中,如果不在,读取到buffer中。
2:在回滚段表空间的相应回滚段事务表中分配事务段,这个操作产生redo信息。
3:从回滚段中读入或者在buffer中或者说是在buffer cache中创建name='old_exitgogo'的前镜像,这个操作同样产生redo信息并记录写入redo buffer。
4:在数据缓冲区修改name='exitgogo',这个操作的日志信息也写入redo buffer。
5:当用户提交时刻,会在redo buffer中记录提交信息,同时会在回滚段中标记该事务为非激活状态(inactive),这点很重要。
可以看到,在一个事务进行过程中,redo和undo是交替出现的,redo buffer会首先记录此事务变化前的数据和变化后的数据,然后把变化前的数据写入回滚段,最后才在数据缓冲区中修改数据。
以下把instance crash后,先forward,再open,再把未提交的rollback的详细过程介绍如下:
当以上这个语句执行到第四步的时候,正巧有某种原因导致oracle要flush buffer cache,比如buffer cache满了等等时间发生,此时,oracle要把脏数据写入磁盘,也就是此时触发了dbwr写进程,又由于dbwr写之前一定要触发lgwr,所以以上语句执行的前4步都会写入redo文件中。
此时,如果发生数据库崩溃,由于没有提交动作发生,也就是没有第五步的操作了那么,在数据库回滚段中标记这个事务将仍为激活状态(active)。
在数据库重新启动过程中,SMON会扫描undo segment header,将发现上面的执行语句还是处于激活状态,于是, 将这些未提交的活动事务标志为dead。
roll forward可能发生在以下两种情况下:
(1):如果以上语句在执行第三步时,数据库crash,那么undo回滚段中记录的此事务的前镜像数据将丢失,数据库在启动过程中,会首先发生rollforward,根据redo文件记录的信息,在回滚段中生成name='old_exitgogo'的前镜像。这也是redo为什么记录undo信息的原因。
(2):如果以上语句提交后,dbwr进程还没有来的及将修改信息写到,此时数据库崩了,那么数据库在重新启动后也要进行roll forward,此时根据redo log文件的记录更新数据文件。也就是让以上语句的更改生效,所以只要提交的数据就不会丢失。
接下来数据库就可以open。
此后有两种情况下将发生回滚:
(1):后台进程SMON发现dead事务,根据情况去逐步回滚。
(2):由于数据库已经open,可能就有很多用户进程访问这些dead事务块。此时这些前台的进程将会去undo segment取得前镜像的数据,例如以上的name='old_exitgogo'这个值,然后修改数据缓冲区,完成回滚。这个过程本身也要产生redo,因此回退这个操作代价很大。
至此,数据库启动过程的前滚以及回退操作完成.