Oracle中联机日志文件(online redo log)在大多平台上以512 字节为一个标准块。
(HPUX,Tru64 Unix上是1024bytes,SCO UNIX,Reliant UNIX上是2048bytes,而MVS,MPE/ix上是4096bytes,虽然以上许多UNIX已经不再流行,实际情况可以通过
select max(l.lebsz) log_block_size_kccle
from sys.x$kccle l
where l.inst_id = userenv('Instance') 语句查询到)
LGWR后台进程写出REDO时未必能填满最后的当前日志块。举例而言,假设redo buffer中有1025字节的内容需要写出,则1025=512+512+1 共占用三个重做日志标准块,前2个标准块被填满而第三个标准块只使用了1个字节。在LGWR完成写出前,需要释放"redo allocation"闩,在此之前SGA中索引"redo buffer"信息的变量将指向未被填满块后面的一个重做块,换而言之有511字节的空间被LGWR跳过了,这就是我们说的redo wastage;我们可以通过分析v$sysstat动态视图中的redo wastage统计信息了解实例生命周期中的重做浪费量。
SQL> col name for a25
SQL> select name,value from v$sysstat where name like '%wastage%';
NAME VALUE
------------------------- ----------
redo wastage 132032
redo wastage的一个图示:
为什么要浪费这部分空间呢?实际上,这种做法十分有益于LGWR的串行I/O模式。redo wastage并不是问题或者Bug,而是Oracle故意为之的。当然过量的redo wastage也不是什么好事,一般是LGWR写出过于频繁的症状表现。9i以后很少有因为隐式参数_log_io_size过小而导致的LGWR过载了,如果在您的系统中发现redo wastage的问题不小,那么无限制地滥用commit操作往往是引起问题的罪魁祸首,减少不必要的commit语句,把commit从循环中移除都将利于减少redo wastage。
我们来看一下关于redo wastage的演示:
SQL> select distinct bytes/1024/1024 from v$log;
BYTES/1024/1024
---------------
50 /*确认联机日志文件大小为50MB*/
SQL> archive log list; /*确认数据库处于归档状态*/
Database log mode Archive Mode
Automatic archival Enabled
Archive destination /s01/arch
SQL> set time on;
19:49:45 SQL> alter system switch logfile; /*切换日志,清理现场*/
System altered.
19:51:07 SQL> col name for a25
19:51:16 SQL> select name,value from v$sysstat where name in ('redo size','redo wastage');
NAME VALUE
------------------------- ----------
redo size 1418793324
redo wastage 88286544 /*演示开始时的基础统计值*/
19:51:19 SQL> begin
19:52:10 2 for i in 1..550000 loop
19:52:10 3 insert into tv values(1,'a');
19:52:10 4 commit;
19:52:10 5 end loop;
19:52:10 6 end;
19:52:11 7 /
/*匿名块中commit操作位于loop循环内,将导致大量redo wastage*/
PL/SQL procedure successfully completed.
19:53:07 SQL> select name,value from v$sysstat where name in ('redo size','redo wastage');
NAME VALUE
------------------------- ----------
redo size 1689225404
redo wastage 112011352
/*频繁提交的匿名块产生了 1689225404-1418793324=257MB的redo,其中存在112011352-88286544=22MB的redo wastage*/
19:53:14 SQL> begin
19:53:33 2 for i in 1..550000 loop
19:53:33 3 insert into tv values(1,'a');
19:53:33 4 end loop;
19:53:33 5 commit;
19:53:33 6 end;
19:53:34 7 /
/* 此匿名块中commit操作被移除loop循环中,批量修改数据后仅在最后提交一次*/
PL/SQL procedure successfully completed.
19:53:59 SQL> select name,value from v$sysstat where name in ('redo size','redo wastage');
NAME VALUE
------------------------- ----------
redo size 1828546240
redo wastage 112061296
/*稀疏提交的匿名块最后产生了1828546240-1689225404=132MB的重做,而redo wastage为112061296-112011352=48k*/
可能您会很奇怪前者不是只比后者多出22MB的redo浪费吗,为什么总的redo量差了那么多?
我们需要注意到commit本身也是要产生redo的,而且其所产生的还不少!就以上演示来看频繁提交的过程中,commit所占用的redo空间几乎接近一半(257-132-22)/257=40%,而每次commit的平均redo量为(257-132-22)*1024*1024/550000=196 bytes。
commit操作是事务ACID的基础之一,合理运用commit可以帮我们构建健壮可靠的应用,而滥用它必将是另一场灾难!