oracle redo与undo_笔记1:什么是undo?
什么是undo
从概念上讲,undo正好与redo相对。对数据库执行修改时,数据库会生成undo信息,以便回到更改前的状态。这样万一执行的事务或语句由于某种原因失败了,或者如果用一条ROLLBACK语句请求回滚,就可以利用这些undo信息将数据放回到修改前的样子。redo用于在失败时重放事务(即恢复事务),undo则用于取消一条语句或一组语句的作用。与redo不同,undo在数据库内部存储在一组特殊的段中,这称为undo段。
通常对undo有一个误解,认为undo用于将数据库物理地恢复到执行语句或事务之前的样子,但实际上并非如此。数据库只是逻辑地恢复到原来的样子,所有修改都被逻辑地取消,但是数据结构以及数据库块本身在回滚后可能大不相同。原因在于:在所有多用户系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要功能之一就是协调对数据的并发访问。也许我们的事务在修改一些块,而一般来讲往往会有许多其他的事务也在修改这些块。因此,不能简单地将一个块放回到我们的事务开始前的样子,这样会撤销其他人(其他事务)的工作!
例如,假设我们的事务执行了一个INSERT语句,这条语句导致分配一个新区段(也就是说,导致表的空间增大)。通过执行这个INSERT,我们将得到一个新的块,格式化这个块以便使用,并在其中放上一些数据。此时,可能出现另外某个事务,它也向这个块中插入数据。如果要回滚我们的事务,显然不能取消对这个块的格式化和空间分配。因此,Oracle回滚时,它实际上会做与先前逻辑上相反的工作。对于每个INSERT,Oracle会完成一个DELETE。对于每个DELETE,Oracle会执行一个INSERT。对于每个UPDATE,Oracle则会执行一个“反UPDATE”,或者执行另一个UPDATE将修改前的行放回去。
怎么才能看到undo生成的具体情况呢?也许最容易的办法就是遵循以下步骤。
(1)创建一个空表
(2)对它做一个全表扫描,观察读表所执行的I/O数量。
(3)在表中填入许多行(但没有提交)
(4)回滚这个工作,并撤销
(5)再次进行全表扫描,观察所执行的I/O数量。
首先,我们创建一个空表然后查询这个表,
scott@ORCL>create table t
2 as
3 select *
4 from all_objects
5 where 1=0;
Table created.
最初,这个查询不需要I/O就能完成这个表的全表扫描:
scott@ORCL>select * from t;
no rows selected
scott@ORCL>set autotrace traceonly statistics
scott@ORCL>select * from t;
no rows selected
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
0 consistent gets
0 physical reads
0 redo size
1343 bytes sent via SQL*Net to client
512 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
scott@ORCL>set autotrace off
对于表的I/O数居然为0。这是因为有了11g Release 2新增的特性——延迟段创建。
接下来,向表中增加大量数据。将它扩展,然后全部回滚。
scott@ORCL>insert into t select * from all_objects;
56308 rows created.
scott@ORCL>rollback;
Rollback complete.
现在,再次查询这个表,会发现这一次读表所需的I/O比先前多得多
scott@ORCL>select * from t;
no rows selected
scott@ORCL>set autotrace traceonly statistics
scott@ORCL>select * from t;
no rows selected
Statistics
----------------------------------------------------------
0 recursive calls
1 db block gets
846 consistent gets
0 physical reads
0 redo size
1343 bytes sent via SQL*Net to client
512 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
scott@ORCL>set autotrace off
前面的INSERT导致将一些块增加到表的高水位线(high-water mark,HWM) 之下,这些块没有因为回滚而消失,它们还在那里,而且已经格式化,只不过现在为空。全表扫描必须读取这些块,看看其中是否包含行。
另外,第一次运行这个查询时,我们观察到I/O数为0。这是由于Oracle Database 11g Release 2中创建表所采用的默认模式——使用延迟段创建(deferred segment creation)。执行这个CREATE TABLE时,不会分配任何存储空间,一个区段都不会分配。要延迟到INSERT发生时才会真正创建段,回滚时,段将持久存储。
scott@ORCL>drop table t;
Table dropped.
scott@ORCL>create table t(x int)
2 segment creation deferred;
Table created.
scott@ORCL>select extent_id,bytes,blocks
2 from user_extents
3 where segment_name='T'
4 order by extent_id;
no rows selected
scott@ORCL>insert into t(x) values(1);
1 row created.
scott@ORCL>rollback;
Rollback complete.
scott@ORCL>select extent_id,bytes,blocks
2 from user_extents
3 where segment_name='T'
4 order by extent_id;
EXTENT_IDBYTES BLOCKS
---------- ---------- ----------
065536 8
可以看到,表创建之后没有分配任何存储空间——这个表没有使用任何区段。完成一个INSERT后,紧接着执行ROLLBACK,可以看到INSERT分配了存储空间,不过ROLLBACK并没有将分配的存储空间释放。
一方面,段确实由INSERT创建但是未被ROLLBACK撤销;另一方面,由INSERT新创建的格式化块被第二次扫描,这两方面结合在一起,说明回滚只是一种逻辑上将数据库恢复原状态的操作。数据库并不会完全恢复原状,只是逻辑上相同而已。
本文来源 我爱IT技术网 http://www.52ij.com/jishu/5250.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
