首页 > 数据库 > Oracle >

《Oracle编程艺术》学习笔记(18)-REDO和UNDO

2011-10-30

REDOOracle维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志文件。在线重做日志用于在出现电源故障(实例终止)时修正数据文件,而归档重做日志用于在出现硬盘故障时或者误操作删...

REDO

Oracle维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志文件。

在线重做日志用于在出现电源故障(实例终止)时“修正”数据文件,而归档重做日志用于在出现硬盘故障时或者误操作删除数据时,配合数据文件备份“修正”数据文件。

为什么需要重做日志文件->http://www.2cto.com/database/201110/108941.html

每个Oracle数据库都至少有两个在线重做日志组,每个组中至少有一个成员(重做日志文件)。这些在线重做日志组以循环方式使用。

可以使用以下语句在一个操作前后查询redo size,计算差值来得到本次操作生成的重做日志的大小:

select b.value from v$statname a, v$mystat b where a.statistic# = b.statistic# and a.name = 'redo size';

(对于简单的DML语句,也可以在SQL*PLUS中利用AUTOTRACE来测量)

UNDO

对数据执行修改时,数据库会生成undo信息,这样事务或语句由于某种原因失败了,或者用ROLLBACK语句请求回滚,就可以利用这些UNDO信息将数据放回到修改前的样子。UNDO在数据库内部存储在一组特殊的段中,这称为UNDO段(UNDO segment),或者“回滚段”(rollback segment)。

需要注意,UNDO并不能将数据库物理地恢复到执行语句或事务之前,只是逻辑地恢复,所有修改都被逻辑地取消,但是数据结构以及数据库块本身在回滚后可能会改变。考虑到可能会有并发事务,必须这样。

UNDO也会受到REDO的保护。换句话说,会把UNDO数据当成是表数据或索引数据一样,对UNDO的修改会生成一些REDO,这些REDO将计入日志。

如何估计REDO量

需要考虑表数据或索引数据的REDO,加上对UNDO的修改生成的REDO,来估算REDO的生成量。

INSERT生成很少的UNDO,但是生成大量的REDO;DELETE生成很少的REDO,但是生成最多的UNDO;UPDATE则同时生成大量的REDO和UNDO。

因此可以如下估算:

· 估计你的“事务”大小(你要修改多少数据)。

· 在要修改的数据量基础上再加10%~20%的开销,具体增加多大的开销取决于要修改的行数。修改行越多,增加的开销就越小。

· 对于UPDATE,要把这个估计值加倍。

UPDATE 的估计值加倍只是一个猜测,实际上这取决于你修改了多少数据。之所以加倍,是因为在此假设要取一个X 字节的行,并把它更新(UPDATE)为另一个X 字节的行。如果你取一个小行(数据量较少的行),要把它更新为一个大行(数据量较多的行),就不用对这个值加倍(这更像是一个INSERT)。如果取一个大行,而把它更新为一个小行,也不用对这个值加倍(这更像是一个DELETE)。加倍只是一种“最坏情况”。

另外还必须考虑到索引,触发器,隐式操作(如外键上的ONDELETE CASCADE设置)等影响。

临时表上的REDO和UNDO

临时表->http://www.2cto.com/database/201110/109554.html

临时表不会为数据块生成redo,但是临时表会生成undo(因为必须支持rollback),由于undo数据必须建立redo日志,因此临时表会为所生成的undo生成一些redo日志。

·INSERT在临时表上只会生成很少的数据,因为临时表只会为UNDO数据建立REDO日志,而INSERT的UNDO很少。

·DELETE在临时表上生成的REDO与正常表上生成的REDO几乎同样多。因为对DELETE的UNDO很大,而REDO很小。

·UPDATE会生成正常表UPDATE一半的REDO。

因此,应当避免删除临时表(可以使用TRUNCAT,或者只是让临时表在COMMIT之后或会话终止时自动置空),把临时表主要用于插入(INSERT)和选择(SELECT),这样就能充分利用临时表不生成REDO的能力。

COMMIT所作的事情

实际上COMMIT之前,已经修改了数据库中的数据,所以99.9%的工作都已经完成。例如,已经发生了以下操作:

· 已经在SGA的块缓存区中生成了undo块。

· 已经在SGA的块缓存区中生成了已修改数据块。

· 已经在SGA的重做日志缓存区中生成了对于前两项的缓存redo。

· 取决于前三项的大小,以及这些工作花费的时间,前面的每个数据(或某些数据)可能已经刷新输出到磁盘。

· 已经得到了所需的全部锁。

执行COMMIT时,余下的工作只是:

·LGWR 将所有余下的缓存重做日志条目写到磁盘,并为事务生成一个SCN(System Change Number or System Commit Nunber,可以把SCN看作一个钟摆,每次有人COMMIT时,SCN都会增1),把SCN记录到在线重做日志文件中。这一步就是真正的COMMIT。事务条目会从V$TRANSACTION 中“删除”,这说明我们已经提交。

·V$LOCK中记录这我们的会话持有的锁都将被释放。

· 如果事务修改的某些块还在缓冲区缓存中,则会以一种快速的模式访问并“清理”。块清除(Block cleanout)是指清除块首部的与锁相关的信息,实质上讲,我们在清除块上的事务信息。

可以看到,处理COMMIT 所要做的工作很少。其中耗时最长的操作要算LGWR的物理磁盘写入操作。不过,实际上由于LGWR一直在后台增量式地刷新输出重做日志缓冲区的内容,即使我们有一个长时间运行的事务生成了大量缓存的重做日志,但在提交之前,许多缓存重做日志已经刷新输出到磁盘了,只需要等待剩余部分输出到磁盘。(默认情况如此,也有例外->http://www.2cto.com/database/201110/108947.html)

可以看出,如果事务提交得太频繁,就有可能引入大量的日志文件同步等待。

因此最好根据业务需求来确定事务的大小,而不是错误地为了减少数据库上的资源使用而“压缩”事务。

ROLLBACK所作的事情

回滚时间是所修改数据量的一个函数,因为ROLLBACK必须物理地撤销我们所做的工作。类似于COMMIT,在到达ROLLBACK之前,数据库已经做了大量的工作,执行ROLLBACK时,需要做如下工作:

· 撤销已做的所有修改。从undo段读回数据,然后逆向执行前面所做的操作,并将undo 条目标记为已用。如果先前插入了一行,将其删除;如果更新了一行,取消更新;如果删除了一行,把它再次插入。

· 会话持有的所有锁都将释放。


摘自 NowOrNever
相关文章
最新文章
热点推荐