传统情况下,当用户发出commit后,用户会话会等待log file sync直到lgwr写完成。LGWR写完成后,通知处于等待log file sync的会话继续处理后面的操作。这个机制保障了事务的持久性,满足了事务ACID的D。但是PL/SQL不是这么工作的:PL/SQL里的commit 操作不会等待lgwr写完成就可以继续处理后面的操作。简单的看个例子:

CODE:

begin
for r in (select id from t1 where mod(id, 20) = 0) loop
update t1 set small_no = small_no + .1 where id = r.id;
commit;
end loop;
end;
/
上面的代码逻辑非常简单,每20行更新一条记录,并且提交一次,表里一共500条数据,一共需要提交25次。运行完成后,我们检查这个会话的统计资料,我们认为的统计信息,应该象下面展示的:

CODE:

user commits (session statistic)      25
messages sent (session statistic)     25
redo synch writes (session statistic) 25
log file sync (session events)        25
messages received (lgwr statistic)    25
redo writes (lgwr statistic)          25
log file parallel write (lgwr events) 25
但是实际情况和我们认为的很不一样:
user commits (session statistic)      25
messages sent (session statistic)      6
redo synch writes(session statistic)   1
log file sync (session events)         1
messages received (lgwr statistic)     6
redo writes (lgwr statistic)           6
log file parallel write (lgwr events)  6
     Lgwr仅仅写了6次(log file parallel write),用户会话仅仅等待了log file sync一次。那意味着会话发出commit命令后,并没有停下来等待lgwr写完成,就继续处理后面的事务了。用户会话没有遵守事务的持久化原则!!如果实例在PL/SQL处理的过程中crash,那么某些提交的事务是不可恢复的。Oracle对此有一个貌似合理的解释,在PL/SQL没有处理完毕之前,你不知道已经提交了多次。Oracle不会使他们可恢复,只有在PL/SQL结束的时候,增加redo sync writes次数和进入log file sync等待。在进行PL/SQL处理期间,不停的查看等待事件,后台看不到任何的log file sync等待。值得注意的是,如果PL/SQL里包含了DBLINK,那么就会使用传统的提交方式,不会产生出这样的“异常”。还有就是统计资料里显示了会话总共向lgwr发送了6次message sent请求(请求写日志),lgwr也接受到了6次message recived信息,并且写了6次(log file parallel write)。你可能会问,到底多久,会话发送一次写请求?需要知道的是,发送写请求前,会话会去持有redo write latch,然后检查lgwr是不是已经在处理写请求了,如果已经在写了,那么不重复向LGWR发送请求了,如果没在写,才会发,因此如果你的磁盘写的速率足够快,那么lgwr就会被post的次数越多,成正比的关系。还有如果你的cpu足够强,那么PL/SQL块loop的时间就足够小,时间小了,那么lgwr被post的次数也就少了,成反比的关系(在磁盘写速率一定的情况下)。
     最后提醒一句:虽然PL/SQL只有在结束的时候才会等待lgwr写完成,产生log file sync等待,但是不要认为,在PL/SQL运行过程中,实例crash掉,此次PL/SQL处理的所有事务就会丢失,不是这样的,只是丢失部分,是pl/sql在运行过程中,会话是发送写请求给lgwr的(message sent),lgwr接收到写请求后,就要写日志,只要是被写进了日志文件的事务就是可恢复的。也就是说,虽然前台没有等待log file sync,但是后台其实一直是在忙着处理你的事务日志的。