6.5 撤消数据和重载数据的区别
这个主题有特殊的意义,因为许多人在描述撤消和重做时都会犯许多错误。下面是一组对比:
● 撤消是临时的,而重做是永久的。撤消数据至少在生成它的事务存在期间是存在的,但可能不长于这个时段。最终,总是会被覆盖。重做的存在时间是不确定的,首先存在于联机日志文件中,之后存在于归档日志文件中。
● 撤消在逻辑层上进行,而重做在物理层上进行。撤消数据是面向行的,根据生成它们的事务对修改进行分组,在撤消段中,所有项都与同一个事务的行相关。重做是面向物理的。更改矢量与行ID指针指向的块中的物理位置相关。日志缓存或日志文件中的连续更改矢量之间没有关系。
● 撤消可以反转更改,而重做可以重复更改。撤消数据提供了反转已提交事务的功能。重做提供了重放已丢失工作的功能
● 撤消位于表空间,而重做位于文件中。撤消数据是数据库中的一个段结构,重做写入操作系统文件。
● 撤消与重做不是相反的。它们有不同的功能。撤消关乎事务的完整性,而重做的作用是防止数据丢失。
配置撤消保留
自动撤消管理总是尽可能长地保存撤消数据。这通常就是我们需要的。但是,有两种情况需要DBA操作:一种情况是需要确保查询总是成功,即使这意味着DML可能失败,另一种情况是闪回查询要扩展使用。
1.配置撤消保留以支持长时间运行的查询
以秒为单位进行设置的参数UNDO_RETENTION通常是可选的。这个参数指定了保留非活动撤消数据的目标,并确定何时将其归类为过期,而不是未过期。例如,如果运行时间最长的查询需要耗时30分钟,那么就应当将该参数设置为1800。随后,Oracle会设法将所有撤消数据至少保留1800秒,从而使查询不会出现因ORA-1555错误而导致的失败。然而,如果没有设置这个参数或者将其设置为零,那么Oracle仍会尽可能长时间地保留撤消数据。控制过期撤消数据重写的算法总是会首先选择重写最早的数据,因此,UNDO_RETENTION参数的最大值总是与撤消表空间的大小一致。
提示:
一些查询可能运行非常长的时间。持续运行几天的查询并不少见。如果打算在处理正常事务的过程中,成功运行这种查询,就需要一个木星大小的撤消表空间。在报告长时间运行的查询时,可能需要考虑限制DML工作负载,或者调整SQL,减少它需要的时间。
如果确保撤消保留(guaranteed undo retention),那么UNDO_RETENTION参数不是可选的。针对撤消的默认操作模式是:对于事务与查询来说,Oracle更倾向于维护事务。如果在查询可能出现ORA-1555失败与事务必定出现ORA-30036失败之间选择撤消表空间的大小,那么Oracle 会通过重写查询所需的撤消数据使指定事务继续进行。换句话说,UNDO_RETENTION是Oracle试图达到的唯一目标。不过,在某些情况下,我们认为成功的查询比成功的事务更重要。例如,对于月末进行填制报表工作的某个公用事业单位来说,完全可以接受事务在生成报告的同时阻滞数小时。使用闪回查询(闪回查询依赖于撤消数据)则是另一个示例。
确保撤消保留意味着在经过UNDO_RETENTION参数所指定的时间之前,撤消数据绝不会被重写。在表空间层次上启用撤消保留(undo retention)。我们既可以在创建撤消表空间时指定这个特性,也可以在后面通过更改撤消表空间来启用该特性。一旦激活某个已被指定确保保留的撤消表空间,如果所有查询都能够在撤消保留时间内结束,那么这些查询都将成功完成,而且也不会再次接收到“snapshot too old”错误。不利的方面是事务可能由于缺乏撤消空间而失败。
如果已经设置了UNDO_RETENTION参数,而且构成撤消表空间的数据文件被设置为自动扩展,那么,Oracle将根据需要自动增加数据文件的大小,来满足撤消保留目标的要求。确保撤消保留和自动扩展数据文件的结合使用意味着,如果磁盘空间足够大,查询和事务始终都能够成功。否则,自动扩展将会失败。
2.闪回查询和撤消保留
闪回查询可以给撤消系统提出更多的要求。闪回查询允许用户查看数据库在以前某个时刻的状态。进行闪回查询有几种方法,但最简单的是使用带AS OF子句的简单SELECT语句。下面是一个例子:
select * from scott.emp as of timestamp (systimestamp - 10/1440);
这个语句返回10分钟前SCOTT.EMP表中的所有行。可以看到已删除的行,看不到插入的新行,已更新的行则显示其旧值。无论是否执行了DML语句,都是如此。要进行闪回查询,应使用撤消数据回滚所有更改。已删除的行从撤消段中提取,并插回结果集;已插入的行从结果集中删除。前面尝试返回10分钟前的查询很可能成功。而试图返回1周前的程序几乎肯定会失败,因为重新构建表在一周前的版本所需的撤消数据已经被覆盖了。
闪回查询是一个很有价值的工具。例如,如果前一个小时的某一刻,删除操作出错(已执行),下面这个命令就可以把所有删除的行插回表中,反转该操作:
insert into scott.emp (select * from scott.emp as of timestamp (systimestamp - 1/24) minus select * from scott.emp);
如果可能使用闪回查询,就必须把UNDO_RETENTION参数设置为合适的值,配置撤消系统,来处理它。如果希望能闪回一天,它就必须设置为86 400秒。撤消表空间必须有合适的大小。接着为了确保成功,应自动扩展撤消表空间的数据文件,或者启用表空间的保留保证。
练习6-7 使用事务和闪回查询
本练习演示撤消数据用于提供事务隔离和回滚的方式,并实现闪回查询。使用HR演示模式中的REGIONS表。下面是要执行的步骤:
(1) 用两个并发会话连接到HR模式。它们可以是两个SQL*Plus 会话、两个SQL Developer会话,或者一个SQL*Plus会话和一个SQL Developer会话。表6-4列出了每个会话中要执行的步骤:
表6-4 执行会话的步骤
提交的事务不能反转,因为它已经提交了,但未提交的事务现在完全消失了,被更改的回滚中断了。所有事务中断后,两个会话就会看到表的一致视图。
(2) 使用一个连接为用户HR的会话,演示闪回查询的用法:
A.调整时间显示格式,使之包含秒数。
alter session set nls_date_format='dd-mm-yy hh24:mi:ss';
B.查询并记录当前时间:
select sysdate from dual;
C.删除以前插入的行,再执行删除操作:
delete from regions where region_id=100; commit;
D.查询删除行之前的表:
select * from regions as of timestamp
to_timestamp('time_from_step_2', 'dd-mm-yy hh24:mi:ss');
结果列出了区域100中已删除的行,它们是从撤消段中检索出来的。