共计 3214 个字符,预计需要花费 9 分钟才能阅读完成。
一. 什么是 redo(用于前滚数据)
redo 也就是重做日志文件(redo log file),Oracle 维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志文件。这两类重做日志文件都用于恢复;其主要目的是,万一实例失败或介质失败,它们能够恢复数据。
二. 什么是 undo(用于回滚数据)
从概念上讲,undo 正好与 redo 相对。你对数据执行修改时,数据库会生成 undo 信息,这样万一你执行的事务或语句由于某种原因失败了,或者如果你用一条 ROLLBACK 语句请求回滚,就可以利用这些 undo 信息将数据放回到修改前的样子。redo 用于在失败时重放事务(即恢复事务),undo 则用于取消一条语句或一组语句的作用。
三. 对 undo 段的一个误解
通常对 undo 有一个误解,认为 undo 用 于数据库物理地恢复到执行语句或事务之前的样子,但实际上并非如此。数据库只是逻辑地恢复到原来的样子,所有修改都被逻辑地取消,但是数据结构以及数据库 块本身在回滚后可能大不相同。(比如一个插入操作,新分配了一些数据块。后来事务失败,插入操作全部回滚,新分配的一些数据块还是存在的)
原因在于:在所有多用户系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要功能之一就是协调对数据的并发访问。也 许我们的事务在修改一些块,而一般来讲往往会有许多其他的事务也在修改这些块。因此,不能简单地将一个块放回到我们的事务开始前的样子,这样会撤销其他人(其他事务)的工作!
例如,假设我们的事务执行了一个 INSERT 语句,这条语句导致分配一个新区段(也就是说,导致表的空间增大)。通过执行这个 INSET,我们将得到一个新的块,格式化这个块以便使用,并在其中放上一些数据。此时,可能出现另外某个事务,它也向这个块中插入数据。如果要回滚我们的事务,显然不能取消对这个块的格式化和空间分配。因此,Oracle 回滚时,它实际上会做与先前逻辑上相反的工作。对于每个 INSERT,Oracle 会完成一个 DELETE。对于每个 DELETE,Oracle 会执行一个 INSERT。对于每个 UPDATE,Oracle 则会执行一个“反 UPDATE“,或者执行另一个 UPDATE 将修改前的行放回去。
所以有一种异常情况就很容易解释了,一个表明明只有 1000 行左右的数据,一条 select * from table 语句可能需要耗时 1,2 分钟。这张表应该是经常进行新增删除操作的表,比如我新增了 1000 万行数据,然后又将这些数据删除。对这个表进行全表扫描的时候,仍然会去扫描这 1000 万行以前所占用的那些数据块,看看里面是否包含数据。也就是 oracle 里面所说的高水平线(HWM),这些数据块都增加到了高水平线下面,oracle 会扫描所有高水平线下的数据块。
redo–> undo–>datafile
insert 一条记录时, 表跟 undo 的信息都会放进 redo 中, 在 commit 或之前, redo 的信息会放进硬盘上. 故障时, redo 便可恢复那些已经 commit 了的数据.
redo-> 每次操作都先记录到 redo 日志中,当出现实例故障(像断电),导致数据未能更新到数据文件,则数据库重启时须 redo,重新把数据更新到数据文件
undo-> 记录更改前的一份 copy,但你系统 rollback 时,把这份 copy 重新覆盖到原来的数据
redo-> 记录所有操作,用于恢复(redo records all the database transaction used for recovery)
undo-> 记录所有的前印象,用于回滚(undo is used to store uncommited data infor used for rollback)
redo-> 已递交的事务, 实例恢复时要写到数据文件去的
undo-> 未递交的事务.
redo 的原因是:每次 commit 时,将数据的修改立即写到 online redo 中,但是并不一定同时将该数据的修改写到数据文件中。因为该数据已经提交,但是只存在联机日志文件中,所以在恢复时需要将数据从联机日志文件中找出来,重新应用一下,使已经更改数据在数据文件中也改过来!
undo 的原因是:在 oracle 正常运行时,为了提高效率,假如用户还没有 commit, 但是空闲内存不多时,会由 DBWR 进程将脏块写入到数据文件中,以便腾出宝贵的内存供其它进程使用。这就是需要 UNDO 的原因。因为还没有发出 commit 语句,但是 oracle 的 dbwr 进程已经将没有提交的数据写到数据文件中去了。
undo 也是也是 datafile,可能 dirty buffer 没有写回到磁盘里面去。
只有先 redo apply 成功了,才能保证 undo datafile 里面的东西都是正确的,然后才能 rollback
做 undo 的目的是使系统恢复到系统崩溃前 (关机前) 的状态, 再进行 redo 是保证系统的一致性.
不做 undo, 系统就不会知道之前的状态,redo 就无从谈起
所以 instance crash recovery 的时候总是先 rollforward,再 rollback
undo
回退段中的数据是以“回退条目”方式存储。
回退条目 = 块信息(在事务中发生改动的块的编号)+ 在事务提交前存储在块中的数据
在每一个回退段中 oracle 都为其维护一张“事务表”
在事务表中记录着与该回退段中所有回退条目相关的事务编号(事务 SCN& 回退条目)
redo
重做记录由一组“变更向量”组成。
每个变更变量中记录了事务对数据库中某个块所做的修改。
当用户提交一条 commit 语句时,LGWR 进程会立刻将一条提交记录写入到重做日志文件中,然后再开始写入与该事务相关的重做信息。
# 事务提交成功后,Oracle 将为该事备生成一个系统变更码(SCN)。事务的 SCN 将同时记录在它的提交记录和重做记录中。
commit
提交事务前完成的工作:
·在 SGA 区的回退缓存中生成该事务的回退条目。在回退条目中保存有该事务所修改的数据的原始版本。
·在 SGA 区的重做日志缓存中生成该事务的重做记录。重做记录中记载了该事务对数据块所进行的修改,并且还记载了对回退段中的数据块所进行的修改。缓存中的重做记录有可能在事务提交之前就写入硬盘中。
·在 SGA 区的数据库缓丰中记录了事务对数据库所进行的修改。这些修改也有可能在事务提交之前就写入硬盘中。
提交事务时完成的工作:
·在为该事务指定的回退段中的内部事务表内记录下这个事务已经被提交,并且生成一个惟一的 SCN 记录在内部事务表中,用于惟一标识这个事务。
·LGWR 后进进程将 SGA 区重做日志缓存中的重做记录写入联机重做日志文件。在写入重做日志的同时还将写入该事务的 SCN。
·Oracle 服务进程释放事务所使用的所有记录锁与表锁。
·Oracle 通知用户事务提交完成。
·Oracle 将该事务标记为已完成。
rollback
回退事务完成的工作:
·Oracle 通过使用回退段中的回退条目,撤销事务中所有 SQL 语句对数据库所做的修改。
·Oracle 服务进程释放事务所使用的所有锁
·Oracle 通知事务回退成功。
·Oracle 将该事务标记为已完成
举个例子:
insert into a(id) values(1);(redo)
这条记录是需要回滚的。
回滚的语句是 delete from a where id = 1;(undo)
试想想看。如果没有做 insert into a(id) values(1);(redo)
那么 delete from a where id = 1;(undo)这句话就没有意义了。
现在看下正确的恢复:
先 insert into a(id) values(1);(redo)
然后 delete from a where id = 1;(undo)
系统就回到了原先的状态,没有这条记录了
更多 Oracle 相关信息见Oracle 专题页面 https://www.linuxidc.com/topicnews.aspx?tid=12
: