再上一篇:8.7小结
上一篇:9.1什么是 redo?
主页
下一篇:9.2.1 redo 和undo如何协作?
再下一篇:9.3提交和回滚处理
文章列表

9.2什么是 undo?

Oracle 9i 10g编程艺术:深入数据库体系结构

从概念上讲,undo正好与redo相对。你对数据执行修改时,数据库会生成undo信息,这样万一你 执行的事务或语句由于某种原因失败了,或者如果你用一条ROLLBACK语句请求回滚,就可以利用这些undo 信息将数据放回到修改前的样子。redo用于在失败时重放事务(即恢复事务),undo则用于取消一条语句 或一组语句的作用。与 redo 不同,undo 在数据库内部存储在一组特殊的段中,这称为 undo 段(undo segment)。
注意 “回滚段”(rollback segment)和“undo段“(undo segment)一般认为是同义词。使用手动 undo管理时,DBA会创建”回滚段“。使用自动undo管理时,系统将根据需要自动地创建和销毁” undo段“。对于这里的讨论来说,这些词的意图和作用都一样。
通常对undo有一个误解,认为undo用 于数据库物理地恢复到执行语句或事务之前的样子,但实际 上并非如此。数据库只是逻辑地恢复到原来的样子,所有修改都被逻辑地取消,但是数据结构以及数据库 块 本身在回滚后可能大不相同。原因在于:在所有多用户系统中,可能会有数十、数百甚至数千个并发事务。 数据库的主要功能之一就是协调对数据的并发访问。也 许我们的事务在修改一些块,而一般来讲往往会有 许多其他的事务也在修改这些块。因此,不能简单地将一个块放回到我们的事务开始前的样子,这样会撤 销其他人 (其他事务)的工作!
例如,假设我们的事务执行了一个INSERT语句,这条语句导致分配一个新区段(也就是说,导致表 的空间增大)。通过执行这个INSET,我们将得到一个新的块,格式化这个块以便使用,并在其中放上一些 数据。此时,可能出现另外某个事务,它也向这个块中插入数据。如果要回滚我们的事务,显然不能取消 对这个块的格式化和空间分配。因此,Oracle回滚时,它实际上会做与先前逻辑上相反的工作。对于每个 INSERT,Oracle会完成一个DELETE。对于每个 DELETE,Oracle会执行一个INSERT。对于每个 UPDATE,Oracle 则会执行一个“反UPDATE“,或者执行另一个UPDATE将修改前的行放回去。
注意 这种undo生成对于直接路径操作(direct path operation)不适用,直接路径操作能够绕过 表上的undo生成。稍后将更详细地讨论这些问题。
怎么才能看到undo生成(undo generation)具体是怎样的呢?也许最容易的方法就是遵循以下步骤 :
(1) 创建一个空表。
(2) 对它做一个全部扫描,观察读表所执行的I/O数量。
(3) 在表中填入许多行(但没有提交)。
(4) 回滚这个工作,并撤销。
(5) 再次进行全表扫描,观察所执行的I/O数量。
首先,我们创建一个空表:
ops$tkyte@ORA10G> create table t
2 as
3 select *
4 from all_objects

5 where 1=0; Table created.
然后查询这个表,这里在SQL*Plus中启用了AUTOTRACE,以便能测试I/O。
注意 在这个例子中,每次(即每个用例)都会做两次全表扫描。我们的目标只是测试每个用例中第 二次完成的I/O。这样可以避免统计在解析和优化期间优化器可能完成的额外I/O。

最初,这个查询需要3个I/O来完成这个表的全表扫描:
ops$tkyte@ORA10G> select * from t;
no rows selected
ops$tkyte@ORA10G> set autotrace traceonly statistics ops$tkyte@ORA10G> select * from t;
no rows selected
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
...

ops$tkyte@ORA10G> set autotrace off
接下来,向表中增加大量数据。这会使它“扩大“,不过随后再将其回滚:
ops$tkyte@ORA10G> insert into t select * from all_objects;
48350 rows created.
ops$tkyte@ORA10G> rollback; Rollback complete.

现在,如果再次查询这个表,会发现这一次读表所需的I/O比先前多得多:
ops$tkyte@ORA10G> select * from t;
no rows selected
ops$tkyte@ORA10G> set autotrace traceonly statistics ops$tkyte@ORA10G> select * from t;
no rows selected
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
689 consistent gets
...
ops$tkyte@ORA10G> set autotrace off

前面的INSERT导致将一些块增加到表的高水位线(high-water mark,HWM)之下,这些块没有因为
回滚而消失,它们还在那里,而且已经格式化,只不过现在为空。全表扫描必须读取这些块,看看其中是 否包含行。这说明,回滚只是一个“将数据库还原“的逻辑操作。数据库并非真的还原成原来的样子,只 是逻辑上相同而已。