加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Oracle的REDO和UNDO深入分析(转)

(2015-05-21 14:40:19)
标签:

oracle

redo

undo

分类: Oracle
来源:http://blog.csdn.net/fw0124/article/details/6899364

一、REDO
    Oracle维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志文件。
    在线重做日志用于在出现电源故障(实例终止)时“修正”数据文件,而归档重做日志用于在出现硬盘故障时或者误操作删除数据时,配合数据文件备份“修正”数据文件。

    每个 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
    临时表不会为数据块生成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一直在后台增量式地刷新输出重做日志缓冲区的内容,即使我们有一个长时间运行的事务生成了大量缓存的重做日志,但在提交之前,许多缓存重做日志已经刷新输出到磁盘了,只需要等待剩余部分输出到磁盘。
可以看出,如果事务提交得太频繁,就有可能引入大量的日志文件同步等待。
因此最好根据业务需求来确定事务的大小,而不是错误地为了减少数据库上的资源使用而“压缩”事务。

六、ROLLBACK所作的事情
回滚时间是所修改数据量的一个函数,因为ROLLBACK必须物理地撤销我们所做的工作。类似于COMMIT,在到达ROLLBACK之前,数据库已经做了大量的工作
· 已经在SGA的块缓存区中生成了undo块。
· 已经在SGA的块缓存区中生成了已修改数据块。
· 已经在SGA的重做日志缓存区中生成了对于前两项的缓存redo。
· 取决于前三项的大小,以及这些工作花费的时间,前面的每个数据(或某些数据)可能已经刷新输出到磁盘。
· 已经得到了所需的全部锁。
执行ROLLBACK时,需要做如下工作:
· 撤销已做的所有修改。从undo段读回数据,然后逆向执行前面所做的操作,并将undo 条目标记为已用。如果先前插入了一行,将其删除;如果更新了一行,取消更新;如果删除了一行,把它再次插入。
· 会话持有的所有锁都将释放。

七、块清除
    数据库块的最前面有一个“开销”空间(overhead),这里会存放该块的一个事务表,对于锁定了该块中某些数据的各个“实际”事务,在这个事务表中都有一个相应的条目。
1)首先当一个事务开始时,需要在回滚段事务表上分配一个事务槽;
2)在数据块头部获得一个ITL事务槽,该事务槽指向回滚段段头的事务槽; 
3)在修改数据之前,需要在回滚段中记录前镜像信息,回滚段头事务槽指向该记录;
4)  锁定修改行,修改行锁定位(lb-lock block)指向ITL事务槽;
5)  数据修改可以进行。
    COMMIT时候Oracle需要将回滚段上的事务表信息标记为非活动,以便空间可以重用;此外所做的一个操作是块清除(Block cleanout),如果事务修改的某些块还在缓冲区缓存中,会清除块首部的ITL事务信息(包括提交标志、SCN等)和锁定信息。
    在与我们的事务相关的提交列表中,Oracle会记录已修改的块列表(每个列表可以有20个块指针),Oracle会根据需要分配多个这样的列表,直至达到某个临界点。如果我们修改的块加起来超过了块缓冲区缓存大小的10%,Oracle 会停止为我们分配新的列表。例如,如果缓冲区缓存设置为可以缓存3,000个块,Oracle 会为我们维护最多300个块。
    COMMIT时,Oracle会通过这些列表找到块,如果块仍在块缓冲区中,Oracle会执行一个很快的清理,这叫做快速块清除(FAST BLOCK CLEANOUT)。
    所以,只要我们修改的块数没有超过缓存中总块数的10%,而且块仍在块缓存区中(如果已经被写回到数据文件上再次读出该数据块进行修改成本过于昂贵),Oracle就会在COMMIT时清理这些块。否则,就会延迟块清除到下次访问该块的时候。通过延迟块清除(DELAYED BLOCK CLEANOUT)可以提高数据库的性能,加快提交操作。
所以如果执行一个大的INSERT、UPDATE或DELETE,影响数据库中的许多块,就有可能在此之后,第一个“接触”块的查询会需要修改某些块首部并把块弄脏,生成REDO日志,会导致DBWR把这些块写入磁盘。
如果Oracle不对块完成这种延迟清除,那么COMMIT的处理可能很长,COMMIT必须重新访问每一个块,可能还要从磁盘将块再次读入(它们可能已经刷新输出)。
在一个OLTP系统中,可能从来不会看到这种情况发生,因为OLTP系统的特点是事务都很短小,只会影响为数不多的一些块。
如果你有如下的处理,就会受到块清除的影响:
· 将大量新数据批量加载到数据仓库中;
· 在刚刚加载的所有数据上运行UPDATE(产生需要清理的块);
· 让别人查询这些数据
比较好的做法是:在批量加载了数据后,通过运行DBMS_STATS实用程序来收集统计信息,就能自然的完成块清除工作。

八、ORA-01555:snapshot too old 错误
    Oracle的多版本模型会使用回滚段(UNDO段)数据依照语句或事务(取决于隔离模式)开始时的原样来重建块。
    回滚段是循环使用的,当事务提交以后,该事务占用的回滚段事务表会被标记为非活动,回滚段空间可以被覆盖重用。

造成ORA-01555错误有三个可能原因:UNDO段太小;提交太频繁;延迟的块清除。
1、UNDO段太小
有2个解决办法,
1)减少查询的运行时间(调优)。应该首先尝试这种方法。这样就能降低对undo段的需求,不需求太大的undo段。
2)允许UNDO表空间扩大,为之留出扩展的空间,并增加UNDO保持时间。
管理系统中的undo有两种方法,自动undo管理(Automatic undo management)和手动undo管理(Manual undo management)。
可以通过undo_management参数来设定(AUTO表示自动管理,MANUAL表示手工管理)。推荐使用自动管理。
在自动管理下,通过undo_retention参数告诉Oracle要把undo保留多长时间,要大于执行运行时间最长的事务所需的时间,可以用V$UNDOSTAT来确定长时间运行的查询的持续时间。另外,要确保磁盘上已经预留了足够的空间,使undo段能根据所请求的undo_retention增大。
Oracle会根据并发工作负载来确定要创建多少个undo段,以及每个undo段应该多大。数据库甚至在运行时可以在各个undo段之间重新分配区段。
2、提交太频繁
如果在读取表的同时,另外的会话在修改这个表,就会同时生成查询所需的undo信息,查询可能会利用这些undo信息来得到读一致视图。如果另外的会话提交了所做的更新,就会允许系统重用刚刚填写的undo段空间,就有可能擦除了旧的undo数据。如果查询随后要用到这些undo信,就会收到一个ORA-01555:snapshot too old错误消息。
所以提交的太频繁是造成ORA-01555错误的一个可能原因(比如对一个块上的10行数据,每次修改1行并提交,就会对这个块生成10次UNDO镜像数据)。
3、延迟的块清除
发生延迟块清除情况下,如果一个块已被修改,下一个会话访问这个块时,可能必须查看最后一个修改这个块的事务是否还是活动的。一旦确定该事务不再活动,就会完成块清除。
Oracle会从块首部的ITL事务槽(该事务槽指向回滚段段头的事务槽),确定前一个事务所用的回滚段,然后试图从这个回滚段首部的事务槽来获得该事务的提交SCN,如果事务的前镜像信息已经被覆盖,并且查询SCN也小于回滚段中记录的最小SCN,那么Oracle将无法判断查询SCN和事务提交SCN的大小,此时出现延迟块清除导致的ORA-01555错误。
可见,要从一个延迟的块清除收到ORA-01555错误,以下条件都必须满足:
· 首先做了一个修改并COMMIT,块没有完成自动块清除(例如修改了太多的块,在SGA块缓冲区缓存的10%中放不下)。
· 其他会话没有接触这些块,而且在我们这个“倒霉”的查询命中这些块之前,任何会话都不会接触它们。
· 从SCN=t1开始开始一个长时间运行的查询,这个查询最后会读到上面所说的未完成块清除的其中的一些块。由于读一致性必须将数据回滚到SCN=t1时的状态。
· 开始查询时,上面执行修改的事务的事务条目可能还在undo段的事务表中。但是查询期间,系统中执行了多个提交。执行事务没有接触上面这些已修改的块(如果确实接触到,就会执行块清除,也就不存在问题了)。由于出现了大量的COMMIT,undo段中的事务表要回绕并重用事务槽,现在最初执行修改的事务在回滚段的事务表中的条目已经被覆盖。
· 由于提交太多,undo段中记录的最低SCN现在超过了t1(高于查询的读一致SCN)。
现在,如果查询到达最初已经修改并提交的某个块,就会遇到麻烦。由于最初执行修改的事务条目已经被覆盖,所以无法查询最初执行修改的事务的SCN,而且undo段中记录的最低SCN现在超过了t1,在这种特殊的情况下,查询无法确定块的COMMIT SCN是大于还是小于t1,也就不清楚查询能否使用这个块映像。这就导致了ORA-01555错误。
如果在一个表执行SELECT时(没有应用其他DML操作)出现了ORA-01555错误,就是因为延迟的块清除引起的。解决办法:
1)减少查询的运行时间(调优)。
2)保证使用的事务“大小适当”。确保没有不必要地过于频繁地提交。
3)使用DBMS_STATS扫描相关的对象,加载之后完成这些对象的清理。由于块清除是极大量的UPDATE或INSERT造成的,所以很有必要这样做。
4)允许UNDO表空间扩大,为之留出扩展的空间,并增加UNDO保持时间。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有