在笔者刚开始写Blog的时候,花了很多精力在HWM高水位线上,也写了几篇浅显的文章。前几天又有一个朋友遇到了类似的问题,突然想到几个相关问题。实验之后,略有收获,仅供分享。
1、环境介绍
笔者选择Oracle 11gR2进行试验,实验数据表T。
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
数据表T为一个空表。
SQL> create table t as select * from dba_objects where 1=0;
Table created
2、rollback操作
第一个思考的问题是,当我们插入一批数据,在没有commit的时候,对应的数据其实是已经写入到内存块的,起码是有对应的redo log信息。那么内存中写入的数据也需要对应分配数据块存储空间。也就是,这个过程中是有分配allocate动作的。
如果我们rollback数据,插入的数据被取消,插入数据不会显示出来。但是对应的空间是不是回收呢?Oracle的世界中,没有公开的代码,只有通过各种实验来验证。
初始数据段segment空间0.06M,进行一次插入动作。
SQL> select bytes/1024/1024 from dba_segments where wner='SYS' and segment_name='T';
BYTES/1024/1024
---------------
0.0625
SQL> insert into t select * from dba_objects;
72757 rows inserted
SQL> select bytes/1024/1024 from dba_segments where wner='SYS' and segment_name='T';
BYTES/1024/1024
---------------
9
插入动作,没有提交commit时候,我们看到段结构已经发生变化,膨胀到了9M。之后,我们rollback。
SQL> rollback;
Rollback complete
SQL> select bytes/1024/1024 from dba_segments where wner='SYS' and segment_name='T';
BYTES/1024/1024
---------------
9
Rollback之后,数据量比定位0,但是由此分配的空间不会回收。Oracle中,逻辑存储空间分配一直是有“覆水难收”的。对于堆表而言,插入数据如果在HWM之上,即使将来删除了数据,空闲空间也是不会回收的。上面实验证明了,即使数据没有真实被持久化commit,空间也是被分配,水位线同样上升。
3、rollback与持续insert
一般insert操作对堆表而言,首先在HWM之下找空闲空间,如果找到就保存其中。如果HWM下面没有空闲空间,就会向上推动HWM,推动空间分配。只有加入append hint,不会在HWM下面找空闲,而直接向上推动HWM。
上面的实验,告诉我们即使数据rollback了,也会有新空间分配,HWM向上推动。那么,如果我们持续的进行insert append操作,每次insert之后都会rollback,会不会出现单向数据表体积增大的情况呢?
SQL> truncate table t;
Table truncated
SQL> insert /*+append */ into t select * from dba_objects where rownum<20000;
19999 rows inserted
SQL> select bytes/1024/1024 from dba_segments where wner='SYS' and segment_name='T';
BYTES/1024/1024
---------------
3
SQL> rollback;
Rollback complete
SQL> insert /*+append */ into t select * from dba_objects;
72757 rows inserted
SQL> select bytes/1024/1024 from dba_segments where wner='SYS' and segment_name='T';
BYTES/1024/1024
---------------
9
SQL> rollback;
Rollback complete
从上面的情况看,当我们一次推动HWM上升,但是被rollback。注意,第一次插入的数据只有20000条,大约3M空间。第二次插入的时候,我们使用append,应该在3M的基础上增加9M,总数为12M。
但是实际情况只是补全了缺乏的6M,总数为9M。
这个现象说明对于insert推动HWM上升之后,如果rollback了。空间虽然算已经分配的,但是第二次在使用的时候不会轻易的进行append推动HWM上升。
4、commit和append
那么,如何实现这样持续的HWM推动呢?问题关键在于commit动作。每次进行append操作之后,如果不是进行rollback而是commit,哪怕事后删除,下次的append同样会推动HWM。
SQL> truncate table t;
Table truncated
SQL> declare
2 i number;
3 begin
4 for i in 1..10 loop
5 insert /*+append*/ into t
6 select * from dba_objects;
7 commit;
8
9 delete t;
10 commit;
11 end loop;
12 end;
13 /
PL/SQL procedure successfully completed
上面持续进行了10次动作,查看segment结构。
SQL> select bytes/1024/1024 from dba_segments where wner='SYS' and segment_name='T';
BYTES/1024/1024
---------------
88
此时一行数据也不存在。
SQL> select count(*) from t;
COUNT(*)
----------
0
5、结论
几个简单的实验,其实里面有不少的启示。我们在设计应用系统的时候,数据库不恰当的使用经常引起HWM异常故障。
那么,HWM究竟应该是如何状态呢?笔者认为,如果设计合理,任何系统中任何数据表的数据都是维持一个相对稳定的数据量,也就是稳定的HWM位置。很多时候,如使用SQL Loader或者批量加载中间表,会引起HWM的位置剧烈变化。HWM剧烈变化的下一步往往也就是系统性能问题的开始。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17203031/viewspace-775453/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/17203031/viewspace-775453/