后悔药undo_undo




01

做错事情的时候,我们经常对自己说的最多的一句话就是:要是当时不这么做就好了,要是能复原就好了。现实中没有后悔药,oracle中有后悔药,这个后悔药就是undo。



02

我们对数据执行修改时,数据库会生成undo信息,以便将来需要的时候可以把数据变更回修改之前的状态。此外,当你执行的事务或语句由于某种原因失败的时候,或者你用一条rollback语句请求回滚时,oracle也需要利用这些undo信息将数据恢复到修改之前的样子。undo用于取消一条语句或一组语句的作用,它是存储在数据库内部一组特殊的段中,称为undo段。



03

undo会将数据库“物理地”恢复到某个语句或事务之前的样子吗?我们例子探究下:

后悔药undo_undo_02

然后查询这个表,我们在slqplus中启用AUTOTRACE,它会报告I/O使用情况。

后悔药undo_undo_03

由于延迟段创建特性,可以看到对表的I/O数为零。所谓的延迟段创建,就是我们在执行create table命令时,数据库不会分配任何存储空间,一个extents也不会。分配空间的动作会延迟到第一个insert动作,此时数据库才会真正创建段。

后悔药undo_undo_04

insert语句会导致Oracle数据库为表t分配更多的数据库,当执行rollback命令时,这些新分配的数据块没有因为回滚而消失,它们还在那里,而且已经格式化(这和Linux中挂载盘有相似性),只不过现在为空。当我们进行第二次全表扫描时,Oracle必须读取这些block,看看其中是否包含数据。



04

下面我们指定延迟段创建表:

sql>drop table t purge;

Table dropped;


sql>CREATE TABLE t(x int) SEGMENT CREATION DEFERRED;

Table created;


SQL>select extent_id,bytes,blocks from user_extents where segment_name='T' order by extent_id;

no rows selected;


SQL>insert into t(x) values (1);

1 row created.


SQL>rollback;

Rollback complete.


SQL>select extent_id,bytes,blocks from user_extents where segment_name='T' order by extent_id;

 EXTENT_ID      BYTES     BLOCKS

---------- ---------- ----------

         0               65536          8

 在此可以看到,表创建之后没有分配任何存储空间-----这个表没有任何extents。当我们执行insert并紧接着执行rollback之后,可以看到insert确实分配了存储空间,不过rollback并没有将分配的存储空间“释放”。



由此可以看出,segment确实由insert创建但是未被rollback" 撤销";另一方面,由insert新创建的块会被第二次查询扫描。因此"后悔药"undo是逻辑地将数据恢复到原来的样子,某些修改会被"逻辑地"取消,但是数据结构以及数据块本身在回滚后可能(与事务或语句开始之前的数据块状态)大不相同