《Oracle编程艺术》学习笔记(21)-ORA-01555:snapshot too old 错误

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

造成ORA-01555错误有三个可能原因:UNDO段太小;提交太频繁;延迟的块清除。

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段之间重新分配区段。

提交太频繁
如果在读取表的同时,另外的会话在修改这个表,就会同时生成查询所需的undo信息,查询可能会利用这些undo信息来得到读一致视图。如果另外的会话提交了所做的更新,就会允许系统重用刚刚填写的undo段空间,就有可能擦除了旧的undo数据。如果查询随后要用到这些undo信,就会收到一个ORA-01555:snapshot too old错误消息。
所以提交的太频繁是造成ORA-01555错误的一个可能原因(比如对一个块上的10行数据,每次修改1行并提交,就会对这个块生成10次UNDO镜像数据)。

延迟的块清除
什么是块清除?http://blog.csdn.net/fw0124/article/details/6901732
发生延迟块清除情况下,如果一个块已被修改,下一个会话访问这个块时,可能必须查看最后一个修改这个块的事务是否还是活动的。一旦确定该事务不再活动,就会完成块清除。
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保持时间。