`
linsea
  • 浏览: 87698 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Redo 与 Undo (2)

阅读更多

 

9.4.6 日志竞争

l         redo放在一个慢速设备上:磁盘表现不佳。该购买速度更快的磁盘了。

l         redo与其他频繁访问的文件放在同一个设备上。redo设计为要采用顺序写,而且要放在专用的设备上。如果系统的其他组件(甚至其他Oracle组件)试图与LGWR同时读写这个设备,你就会遭遇某种程度的竞争。在此,只要有可能,你就会希望确保LGWR拥有这些设备的独占访问权限。

l         已缓冲方式装载日志设备。你在使用一个“cooked”文件系统(而不是RAW磁盘)。操作系统在缓冲数据,而数据库也在缓冲数据(重做日志缓冲区)。这种双缓冲会让速度慢下来。如果可能,应该以一种“直接”方式了装载设备。具体操作依据操作系统和设备的不同而有所变化,但一般都可以直接装载。

l         redo采用了一种慢速技术,如RAID-5RAID-5很合适读,但是用于写时表现则很差。前面已经了解了COMMIT期间会发生什么,我们必须等待LGWR以确保数据写到磁盘上。倘若使用的技术会导致这个工作变慢,这就不是一个好主意。

 

9.4.7 临时表和redo/undo

临时表不会为它们的块生成redo。因此,对临时表的操作不是“可恢复的” 。修改临时表中的一个块时,不会将这个修改记录到重做日志文件中。不过,临时表确实会生成 undo,而且这个 undo 会计入日志。因此,临时表也会生成一些redo。初看上去好像没有道理:为什么需要生成undo?这是因为你能回滚到事务中的一个 SAVEPOINT。由于undo数据必须建立日志,因此临时表会为所生成的undo生成一些重做日志。这样似乎很不好,不过没有你想像中那么糟糕。在临时表上运行的 SQL 语句主要是 INSERT SELECT。幸运的是,INSERT 只生成极少的 undo(需要把块恢复为插入前的“没有”状态,而存储“没有”不需要多少空间),另外SELECT根本不生成undo

注意:

l         对“实际”表(永久表)的 INSERT 生成了大量 redo。而对临时表几乎没有生成任何 redo。这是有道理的,对临时表的INSERT只会生成很少的undo数据,而且对于临时表只会为undo数据建立日志。

l         实际表的UPDATE生成的redo大约是临时表更新所生成redo的两倍。同样,这也是合理的。必须保存UPDATE的大约一半(即“前映像”)。对于临时表来说,不必保存“后映像”(redo)。 

l         DELETE 需要几乎相同的redo空间。这是有道理的,因为对DELETE undo很大,而对已修改块的redo很小。因此,对临时表的DELETE与对永久表的DELETE几乎相同。

因此,关于临时表上的DML 活动,可以得出以下一般结论:

l         INSERT 会生成很少甚至不生成undo/redo活动。

l         DELETE 在临时表上生成的redo与正常表上生成的redo同样多。

l         临时表的UPDATE会生成正常表UPDATE一半的redo

有了以上了解,你可能会避免删除临时表。 可以使用TRUNCATE (当然要记住, TRUNCATE DDL,而 DDL 会提交事务,而且在 Oracle9i 及以前版本中,TRUNCATE 还会使你的游标失效) ,或者只是让临时表在 COMMIT 之后或会话终止时自动置空。执行方法不会生成 undo,相应地也不会生成 redo。你可能会尽量避免更新临时表,除非由于某种原因必须这样做。你会把临时表主要用于插入(INSERT)和选择(SELECT) 。采用这种方式,就能更优地使用临时表不生成redo的特有能力。

 

9.5分析 undo 

9.5.1什么操作会生成最多和最少的 undo

一般来讲,INSERT 生成的 undo 最少,因为 Oracle 为此需记录的只是要“删除”的一个rowid(行ID) 。UPDATE 一般排名第二(在大多数情况下)。DELETE生成的 undo最多。与加索引列的更新相比,对一个未加索引的列进行更新不仅执行得更快,生成的 undo 也会好得多。而更新有索引的列则可能生成大量的undo,因为索引结构本身所固有的复杂性,而且我们更新了这个表中的每一行,移动了这个结构中的索引键值。

 

9.5.2 ORA-01555: snapshot too old错误

注意 ORA-01555 与数据破坏或数据丢失毫无关系。在这方面,这是一个“安全”的错误;惟一的影响是:接收到这个错误的查询无法继续处理。

 

这个错误实际上很直接,其实只有两个原因,但是其中之一有一个特例,而且这种特例情况发生得如此频繁,所以我要说存在3 个原因:

l         undo段太小,不足以在系统上执行工作。

l         你的程序跨COMMIT 获取(实际上这是前一点的一个变体)。我们在上一章讨论了这种情况。

l         块清除。

 

在充分说明这三种情况之前,我想先与你分享ORA-01555错误的几种解决方案,一般来说可以采用下面的方法:

l         适当地设置参数 UNDO_RETENTION(要大于执行运行时间最长的事务所需的时间)。可以用V$UNDOSTAT来确定长时间运行的查询的持续时间。另外,要确保磁盘上已经预留了足够的空间,使undo 段能根据所请求的UNDO_RETENTION增大。

l         使用手动 undo 管理时加大或增加更多的回滚段。这样在长时间运行的查询执行期间,覆盖undo数据的可能性就能降低。这种方法可以解决上述的所有3个问题。

l         减少查询的运行时间(调优)。如果可能的话,这绝对是一个好办法,所以应该首先尝试这种方法。这样就能降低对 undo 段的需求,不需求太大的 undo 段。这种方法可以解决上述的所有3个问题。

l         收集相关对象的统计信息。这有助于避免前面所列的第三点。 由于大批量的UPDATEINSERT会导致块清除(block cleanout) ,所以需要在大批量UPDATE或大量加载之后以某种方式收集统计信息。

 

对于Oracle9i 和以上版本,管理系统中的undo有两种方法:

l         自动undo管理 (Automatic undo management):采用这种方法, 通过UNDO_RETENTION参数告诉 Oracle 要把 undo 保留多长时间。Oracle 会根据并发工作负载来确定要创建多少个undo段,以及每个undo段应该多大。数据库甚至在运行时可以在各个undo段之间重新分配区段,以满足DBA 设置的UNDO_RETENTION目标。这是undo管理的推荐方法。

l         手动undo管理(Manual undo management) :采用这种方法的话,要由DBA 来完成工作。DBA 要根据估计或观察到的工作负载, 确定要手动地创建多少个undo 段。 DBA 根据事务量 (生成多少undo)和长时间运行查询的长度来确定这些undo段应该多大。

 

在手动 undo 管理的情况下,DBA 要确定有多少个 undo 段,以及各个 undo 段有多大,这就产生了一个容易混淆的问题。有人说: “那好,我们已经配置了 XMB undo,但是它们可以增长。我们把MAXEXTENTS 设置为 500,而且每个区段是 1MB,所以 undo 可以相当大。”问题是,倘若手动地管理undo段,undo段从来不会因为查询而扩大;只有INSERTUPDATE DELETE才会让undo段增长。事实上,如果执行一个长时间运行的查询,Oracle不会因此扩大手动回滚段(即手动管理的回滚段)来保留数据,以备以后可能需要用到这些数据。只有当执行一个长时间运行的UPDATE 事务时才会扩大手动回滚段。在前面的例子中,即使手动回滚段有增长的潜力,但它们并不会真正增长。对于这样一个系统,你需要有更大的手动回滚段(尽管它们已经很大了)。你要永久地为回滚段分配空间,而不是只给它们自行增长的机会。对于这个问题,惟一的解决方案只能是适当地设置手动回滚段的大小。

 

在自动undo 管理的情况下,从ORA-01555角度看,问题则要容易得多。无需自行确定undo空间有多大并完成预分配,DBA 只有告诉数据库运行时间至少在这段时间内保留 undo。如果已经分配了足够的空间可以扩展,Oracle 就会扩展 undo 段,而不是回绕,从而满足 UNDO_RETENTION 保持时间的要求。这与手动管理的 undo 截然相反,手动管理是会回绕,并尽可能块地重用 undo 空间。这是由于这个原因(即自动undo管理支持UNDO_RETENTION参数) ,所以我强烈建议尽可能采用自动undo 管理。

使用手动undo管理时,还要记住重要的一点,遇到ORA-01555错误的可能性是由系统中最小的回滚段指示的(而非最大的回滚段,也并非平均大小的回滚段)。增加一个“大”回滚段不能解决这个问题。处理查询时只会让最小的回滚段回绕,这个查询就有可能遇到 ORA-01555 错误。使用遗留的回滚段时我主张回滚段大小要相等,以上就是原因所在。如果回滚段的大小都相等,那么每个回滚段即是最小的,也是最大的。这也是我为什么避免使用“最优大小”回滚段的原因。如果你收缩一个此前被扩大的回滚段,就要丢掉以后可能还需要的大量 undo。倘若这么做,会丢掉最老的回滚数据,从而力图使风险最小,但是风险还是存在。我喜欢尽可能在非高峰期间手动地收缩回滚段。

    我们已经讨论过块清除机制,不过这里可以做一个总结:在块清除过程中,如果一个块已被修改,下一个会话访问这个块时,可能必须查看最后一个修改这个块的事务是否还是活动的。一旦确定该事务不再活动,就会完成块清除,这样另一个会话访问这个块时就不必再历经同样的过程。要完成块清除, Oracle会从块首部确定前一个事务所用的undo段,然后确定从 undo 首部能不能看出这个块是否已经提交。可以用以下两种方式完成这种确认。一种方式是Oracle可以确定这个事务很久以前就已经提交,它在undo段事务表中的事务槽已经被覆盖。另一种情况是COMMIT SCN还在 undo段的事务表中,这说明事务只是稍早前刚提交,其事务槽尚未被覆盖。

    要从一个延迟的块清除收到ORA-01555错误,以下条件都必须满足:

l         首先做了一个修改并COMMIT,块没有自动清理(即没有自动完成“提交清除” ,例如修改了太多的块,在SGA块缓冲区缓存的10%中放不下) 。

l         其他会话没有接触这些块,而且在我们这个“倒霉”的查询(稍后显示)命中这些块之前,任何会话都不会接触它们。

l         开始一个长时间运行的查询。这个查询最后会读其中的一些块。这个查询从SCN  t1开始,这就是读一致 SCN,必须将数据回滚到这一点来得到读一致性。开始查询时,上述修改事务的事务条目还在undo段的事务表中。

l         查询期间,系统中执行了多个提交。执行事务没有接触执行已修改的块(如果确实接触到,也就不存在问题了)。

l         由于出现了大量的COMMITundo 段中的事务表要回绕并重用事务槽。最重要的是,将循环地重用原来修改事务的事务条目。另外,系统重用了 undo 段的区段,以避免对 undo 段首部块本身的一致读。

l         此外,由于提交太多,undo段中记录的最低SCN 现在超过了t1(高于查询的读一致SCN)。 

 

如果查询到达某个块,而这个块在查询开始之前已经修改并提交,就会遇到麻烦。正常情况下,会回到块所指的undo段,找到修改了这个块的事务的状态(换句话说,它会找到事务的COMMIT SCN)。如果这个 COMMIT  SCN 小于 t1,查询就可以使用这个块。如果该事务的 COMMIT  SCN 大于 t1,查询就必须回滚这个块。不过,问题是,在这种特殊的情况下,查询无法确定块的COMMIT SCN是大于还是小于t1。相应地,不清楚查询能否使用这个块映像。这就导致了ORA-01555错误。

 

万一你发现遭遇了这个问题,即选择(SELECT)一个表时(没有应用其他DML操作)出现了ORA-01555错误,能你可以试试以下解决方案:

l         首先,保证使用的事务“大小适当”。确保没有不必要地过于频繁地提交。

l         使用 DBMS_STATS 扫描相关的对象,加载之后完成这些对象的清理。由于块清除是极大量的UPDATE INSERT造成的,所以很有必要这样做。

l         允许undo表空间扩大,为之留出扩展的空间,并增加undo保持时间。这样在长时间运行查询期间,undo 段事务表中的事务槽被覆盖的可能性就会降低。针对导致ORA-01555错误的另一个原因(undo 段太小) ,也同样可以采用这个解决方案(这两个原因有紧密的关系;块清除问题就是因为处理查询期间遇到了 undo 段重用,而 undo 段大小正是重用 undo 段的一个根本原因) 。实际上,如果把undo表空间设置为一次自动扩展1MB,而且undo保持时间为900秒,再运行前面的例子,对表BIG 的查询就能成功地完成了。

l         减少查询的运行时间(调优)。如果可能的话,这总是件好事,所以应该首先尝试这样做。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics