一直被回退段和重做日志缓冲区所困扰,现在总算有些明白,现在总结一下,其中的错误请大家指正。从名字上看就可以看出回退段主要用于UNDO的,而重做日志缓冲区主要用于REDO的。Oracle崩溃恢复步骤如下:  首先rolling forward 前滚:由于oracle failure,sga中的内存信息丢失了,但是online redo log中还是存储了transaction信息,包括commited or uncommited data。可能这些修改信息并没有被oracle正确的来处理,包含两种情况:已经提交的还没有写入数据文件,或者没有提交的却被写入了数据文件。针对已经提交的还没有写入数据文件就要发生前滚,在前滚过程中,smon会根据online redo log中的记录来完成对datafile的修改。保证已经提交的数据已经写入数据文件。  接下来,前滚结束后,数据库正常open,此时用户可以正常连接,可以访问已经recover的commited data,但是对于那些属于unrecoverable transaction的uncommited data,会被oracle 加锁,是不可以访问的。  rolling back:假如有进程访问这些加锁的data,此时smon会对这些数据块做rollback回滚,从数据文件中撤销没有提交却被写入数据文件的数据。 回退段:把未提交事务的更改恢复到事务开始前的状态。 内部结构:退段中的数据是以“回退条目”方式存储。
回退条目=块信息(在事务中发生改动的块的编号)+在事务提交前存储在块中的数据在每一个回退段中oracle都为其维护一张“事务表”
在事务表中记录着与该回退段中所有回退条目相关的事务编号(事务SCN&回退条目)undo的原因:在oracle正常运行时,为了提高效率,加入用户还没有commit,但是空闲内存不多或设置了CHECKPOINT时,会由DBWR进程将脏块写入到数据文件中,以便腾出宝贵的内存供其它进程使用。这就是需要UNDO的原因。因为还没有发出commit语句,但是oracle的dbwr进程已经将没有提交的数据写到数据文件中去了,此时数据文件中存放commit和uncommit的数据。也就是说即使数据已经写入了数据文件,仍然能回滚,只要回滚段的内部事务表标记为active(事务未提交),若标记为inactive(事务已提交)就不能回滚了。事务分配策略:oracle基于两个原则给事务分配回滚段。首先oracle试图将一个新的事务指派给某个拥有最少活动事务数的回滚段;如果没有单个段能满足这个需求,那么该事务将被指派给某个段来保存undo信息,以便此undo信息能够尽可能长的时间内被用于读一致性视图保留。事务可以用以下语句申请指定的回滚段:SET TRANSTRACTION USE ROLLBACK SEGMENT rollback_segment事务将以顺序,循环的方式使用回滚段的区(EXTENTS),当当前区用满后移到下一个区。几个事务可以写在
回滚段的同一个区,但每个回滚段的块只能包含一个事务的信息。回滚段的扩张(EXTEND):当当前回滚段区的所有块用完而事务还需要更多的回滚空间时,回滚段的指针将移到下一个区。当最后一个区用完,指针将移到第一个区的前面。回滚段指针移到下一个区的前提是下一个区没有活动的事务,同时指针不能跨区。当下一个区正在使用时,事务将为回滚段分配一个新的区,这种分配称为回滚段的扩展。回滚段将一直扩展到该回滚段区的个数到达回滚段的参数MAXEXTENTS的值时为止。
  重做日志缓冲区:把已提交事务的更改写到数据文件内部结构:数据库的更改的最小记录单位是变更向量,一连串的变更向量集合起来称为重做记录 (REDO Record)。每个变更向量中记录了事务对数据库中某个块所做的修改。包括修改对象、前值、后值、该修改操作的事务号和该事务是否已提交等信息。有些事务(transaction)会产生不止一个重做记录。redo的原因:每次commit时,将数据的修改立即写到online redo中,但是并不一定同时将该数据的修改写到数据文件中。因为该数据已经提交,但是只存在联机日志文件中,所以在恢复时需要将数据从联机日志文件中找出来,重新应用一下,使已经更改数据在数据文件中也改过来!发生时刻:对于数据库内所有被更改的数据块(segment),Oracle会把所有更改内容清楚记录在REDO日志缓冲中。 所谓所有更改内容,当然包括数据段,还有索引段和回滚段(rollback segment)。 数据库内任意数据块所发生的一个更改,会被写成一个变更向量(Change Vector)。 例子:──SQL语句──
*************************************************************
UPDATE WORK03
SET EMPNO = 9999
WHERE EMPNO = 1111 ;※ EMPNO项目尚未创建索引
*************************************************************
运行上面的UPDATE语句之后,会产生下面的变更向量。1. 对于回滚段的事务表(标题)的变更向量
当含有修改的数据块的地址、该事务的状态(commit或active)、 以及存有该事务的UNDO的回滚段的位置的事务表被修改的时候,就会产生变更向量。2. 对于回滚段的数据块的变更向量
将修改前的值(1111)存储(修改)到回滚段里的数据块时,就会产生变更向量。 3. 对于WORK03表内的数据块的变更向量
将修改后的值(9999)覆盖(修改)到WORK03表内的数据块时,就会产生变更向量。由上面的例子可知,对于这个事务,重做记录理会有三个修改向量。当然可能有其他情况会产生重做记录, 例如修改的项目如果有索引,就必须修改索引,这时候就会产生第二个重做记录。 这时候的重做记录还是和第一个重做记录一样,包含不止一个变更向量。此外, 在事务之后运行commit语句,就会产生第三个重做记录。 那么,重做日志缓冲区中的内容何时才被写进重做日志文件中呢,当满足以下4个条件任一条时:
(1).Commit操作
(2).Redo buffer log 使用超过1/3
(3).Redo buffer log 使用超过 1 MB
(4).在DBWR开始写之前LGWR写进程就会被触发,从而把重做日志缓冲区中的内容写进重做日志文件。 COMMIT时两者的动作(纯属个人理解): 下面看一下,当执行"UPDATE emp SET sal=2000 WHERE empno=7788;"这条语句时的过程:
   1. Oracle将emp表中empno=7788的记录的sal的值的变化记录到重做日志缓冲区中(也把UNDO段的变更记录到重做日志缓冲区中) 
   2.  将旧值1000记录到UNDO段所对应的缓冲区中;   3. 在为该事务指定的回退段中的内部事务表内记录下这个事务已经被提交,并且生成一个惟一的SCN记录在内部事务表中,用于惟一标识这个事务;(表明事务已提交的数据无法undo,只能作redo操作)  4.将新值2000存放到EMP段所对应的数据高速缓冲区中;
  5.将重做日志缓冲区的内容写入重做日志文件;  6.Oracle服务进程释放事务所使用的所有记录锁与表锁;  7.Oracle通知用户事务提交完成;  8.Oracle将该事务标记为已完成;  9.Commit,写入到数据文件。无论是写入缓冲区还是文件中,都是遵循先写入UNDO段、重做日志缓冲区,再写入数据高速缓冲区;先写入重做日志文件,再写入数据文件。  当出现修改时,对数据库的所有修改(含所有段)首先写入重做日志缓冲区,当后面操作时数据丢失时可做重做操作。然后才是写入回退段,记录肯能要回退的修改,若事务已经提交,则回退段的内部事务表会有标记,表示相应的回退条目不参与回退操作。然后才是写重做日志文件和数据文件。  不明白处:当变更向量写入重做日志缓冲区后,数据库出现问题倒是内存数据消失,那就来不及写入重做日志文件了,那如何才能恢复数据呢。还有能否根据重做日志缓冲区的记录来redo?既然重做日志缓冲区已经记录了所有的修改记录,那么为什么不直接用它就好了,还要用回滚段呢?
   希望大家为我解答,多谢!!

解决方案 »

  1.   

    1
    重做日期缓冲区也是属于内存的,当系统发生故障的时候里面的部分内容应该也是会丢失的,也就是说这部分数据时没办法恢复的。不过LGWR这个进程会频繁的将LOG BUFFER里的内容写入LOG FILE,所以说即便发生你说的这种情况,那么丢失的数据量也是很少的。
    注意到红色字体标出来的部分内容,因为有些在重做日期缓冲区内的内容是已经写到LOG FILE里了,比如说已经COMMIT了的那部分。触发LGWR把日志内容从LOG BUFFER写到LOG FILE的条件有很多,楼主可以去百度一下。2
    回滚段记录的是修改的数据的前镜像,而重做日志缓冲区记录的就是你当前的操作。
    举个例子,可能会形象点
    SQL> select * from t2;        ID
    ----------
             1
    这个时候你执行一条更新语句
    update t1 set id=3;
    这个时候重做日志缓冲区里存的就是你这条UPDATE语句,如果你想回滚的话,你怎么回滚?你不知道这个ID以前的值是1呀,你只能知道修改后的值是3而已。
    而回滚段里就存有数据的前镜像,也就是id=1这个值。
      

  2.   

    多谢您的回复。今天上网查阅了一些资料,发现跟你说的有出入。
    1.只有把重做日志缓冲区的重做记录成功写入重做日志文件中后,才被认为事务成功提交,若出现1的问题,那么事务就是提交失败,所以不存在REDO问题。并且写入重做日志文件的记录含COMMIT和UNCOMMIT的,没有插入提交记录的事务相关重做记录是不会引起redo操作的。
      

  3.   

    我没看太明白你的意思
    好像没什么出入呀
    你就直接说出你的疑问在哪里好了1
    当用户COMMIT成功了的时候 并不能保证这个时候被修改的数据就已经写入数据文件 COMMIT成功只是保证用户的操作已经写入到LOG FILE里了。这个好像和你在网上查到的资料没什么区别吧。
    2
    关于REDO LOG里的内容 确实是有COMMITED何UNCOMMITED的两部分
    因为不仅仅是COMMIT才会触发LGWR把REDO 写入到REDO LOG中 还有一些其他条件会触发这个写的操作我让你去百度一下LGWR触发的条件你没百度吧?呵呵
      

  4.   


    LGWR是负责把Redo Log buffer写入Redo file的进程,当这个进程启动的时候,会把redo buffer里已经有的redo record写入redo file,而当用户commit或者rollback的时候,会触发这个进程,从而保证用户的提交的数据安全,只有写入到redo file,才能保证这个操作是可以恢复的。同时,你上面写的LGWR的4个条件,也不完整,
    当DBWn启动,而且发现要写的脏数据还没有写入redo file的时候,也会先启动LGWR,等待写入Redo file才即系执行的。你说的问题1是指的什么意思呀?
      

  5.   


    不明白处:当变更向量写入重做日志缓冲区后,数据库出现问题倒是内存数据消失,那就来不及写入重做日志文件了,那如何才能恢复数据呢。还有能否根据重做日志缓冲区的记录来redo?既然重做日志缓冲区已经记录了所有的修改记录,那么为什么不直接用它就好了,还要用回滚段呢? 你前面已经提到了,数据库恢复的动作是,先利用redo log file前滚,然后利用undo回滚。所以这里如果是没有进入redo log,所以这里的数据也是无法恢复的。也就是刚刚启动LGWR却还没有完全写入的这块log buffer里的数据。你这里提到的 还有能否根据重做日志缓冲区的记录来redo  不知道你想表达的是什么意思既然重做日志缓冲区已经记录了所有的修改记录,那么为什么不直接用它就好了,还要用回滚段呢
    redo是前滚,把内存里恢复到上次崩溃的时候的脏数据的情况,但是我们知道崩溃的时候,这里的脏数据是还没有commit的,所以需要用undo去rollback回来。
      

  6.   

    问题1,是没有正确理解什么时候才是真正的提交。问题1的情况属于提交失败,所以就算以前有部分重做记录已经记录到重做日志文件中,也不会作恢复操作,而是作undo操作。
      

  7.   

    但是online redo log中还是存储了transaction信息,包括commited or uncommited data。
    问题是:当redo的写条件(1.每3秒超时 2.阀值达到 3.在DBWN写之前)把redobuffer中的未提交的事务写入到 online redo log 中后,当用户提交此事务时候,Oracle通过什么手段把此事的与online redo logfile 中记录的因redo 写条件,而写入online redo logfile中的文件同步。还是oracle通过什么其他的方式来实现。希望高手能给小弟解惑
      

  8.   

    参考一下这个。http://blog.csdn.net/inthirties/archive/2009/11/20/4843831.aspx