转自:http://biancheng.dnbcw.info/oracle/309191.html
Rollback后undo到底做了些什么?
从概念上讲,undo正好与redo相对。当你对数据执行修改时,数据库会生成undo信息,这样万一你执行的事务或语句由于某种原因失败了,或者如果你用一条rollback语句请求回滚,就可以利用这些undo信息将数据还原到修改前的样子。Redo用于在失败时还原事务(即恢复事务),undo则用于取消一条语句或者一组语句的作用。与redo不同,undo在数据库内部存储在一组特殊的段中,这称为undo段(undo segment)。
通常对undo有一个误解,认为undo用于将数据库物理地恢复到执行语句或者事务之前的样子,但实际上并非如此。数据库只是逻辑地恢复到原来的样子,所有修改都被逻辑地取消,但数据结构以及数据库块本身在回滚后可能大不相同。原因在于:在所有的多用户系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要功能之一就是协调对数据的并发访问。也许我们的事务在修改一些块,而一般来讲往往会有许多其他的事务也在修改这些块。因此,不能简单地将一个块放回到我们事务开始前的状态,这样会撤销其他人(其他事务)的工作!
例如,我们的事务执行一个insert语句,这条语句导致分配一个新区段(也就是说,导致表的空间增大)。通过执行这个insert,我们将得到一个新的块,格式化这个块以便使用,并在其中放上一些数据。此时,可能出现某个事务,他也向这个块中插入数据。如果要回滚我们的事务,显然不能取消对这个块的格式化和空间分配。因此,oracle回滚时,它实际上会做与先前逻辑上相反的工作。对于每个insert,oracle会完成一个delete。对于每个delete,oracle会执行一个insert。对于每个update,oracle则会执行一个“反update”,或者执行另一个update将修改的行放回去。当然这种undo生成对于直接路径操作(direct path operation)不适用,直接路径操作能够绕过表上的undo生成。
为了更好的说明问题,下面做一个小小的测试,以便验证上述的正确性。以下就是一个简单测试的步骤:
(1) 创建一个新的空表。
(2) 对它做一个全表扫描,观察对该表所执行的I/O数量。
(3) 向表中填入许多行(暂时不提交commit)
(4) 回滚这个工作。
(5) 再次进行全表扫描,观察对该表所执行的I/O数量。
好了,下面我们一起来做一个简单的测试工作:
首先,创建一个空表:
[email protected]>create table t
2 as
3 select *
4 from all_objects
5 where 1=0;
表已创建。
[email protected]>select * from t;
未选定行
[email protected]>set autotrace traceonly statistics
[email protected]>select * from t;
未选定行
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
915 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
[email protected]>set autotrace off
[email protected]>insert into t select * from all_objects;
已创建31620行。
[email protected]>rollback;
回退已完成。
[email protected]>select * from t;
未选定行
[email protected]>set autotrace traceonly statistics
[email protected]>select * from t;
未选定行
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
436 consistent gets
0 physical reads
0 redo size
915 bytes sent via SQL*Net to client
372 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
前面的insert 导致将一些块增加到表的高水位线(high-water mark,HWM)之下,这些块没有因为rollback而消失,它们还在那里并且已经格式化,只不过现在为空。全表扫描必须读取这些块,看看其中是否包含行。通过比较上面对同一表查询的两个统计信息我们得出:undo操作只是回滚了数据库的逻辑结构,而没有撤销其物理结构。
以上是本人近期对undo的研究之一,写下自己的学习心得,供大家讨论。限于本人学识水平,难免存在问题,请批评指正。如有异议请email:[email protected]。