今天看了tom大师关于sql restart的博客,收益颇深。鉴于翻墙的繁琐和以后查阅的方便,将主要内容总结至此。大家也可以通过如下地址查看原博文及精彩点评(需要翻墙哦)。
http://tkyte./2005/08/something-different-part-i-of-iii.html
http://tkyte./2005/08/part-ii-seeing-restart.html
http://tkyte./2005/09/part-iii-why-is-restart-important-to.html
大家都知道oracle通过一致性读来防止阻塞并为查询提供结果。但是对于写或者说编辑操作,oracle是怎样处理的那?例如下面的语句: update t set x=2 where y=5; 对于这样的语句,oracle首先会根据语句的运行起始时间T1和WHERE条件进行基于T1的一致性查询操作,找出y=5的所有记录,然后对这些记录进行更新操作。那么这里就存在一个问题,假设在T2时间,oracle修改到记录R,如果在T1和T2之间的时间段里,某个用户进程修改了记录R并进行了提交,此时oracle会做如何处理那?我们知道,读操作我们可以基于以往时间点构造一致性块,但是对于写操作,我们必须对当前块进行操作。
首先我们在表tab上创建触发器
SQL> create or replace trigger tab_tri
2 before update on tab for each row
3 begin
4 dbms_output.put_line(' old.x = '||:old.x||',old.y ='||:old.y);
5 dbms_output.put_line(' new.x = '||:new.x||',new.y ='||:new.y);
6 end;
7 / session1:更新某记录,不提交
SQL> update tab set x=2 where y=1; session2:更新相同记录,进程被阻塞
SQL> update tab set x=3 where y=1; session2:session1提交后
SQL> update tab set x=3 where y=1;
old.x = 1,old.y =1
new.x = 3,new.y =1
old.x = 2,old.y =1
new.x = 3,new.y =1
已更新 1 行。 从这里可以看出,如果更新R时,oracle发现R已经被修改过,那么oracle会重新执行update语句。
下面让我们修改一下触发器
SQL> create or replace trigger tab_tri
2 before update on tab for each row
3 begin
4 dbms_output.put_line('aaaaa');
5 end;
6 / session2:在session1提交后:
SQL> update tab set x=3 where y=1;
aaaaa
已更新 1 行。 非常奇怪?这里update语句并没有重新执行 下面在来改变一下执行语句: sesion1:
SQL> update tab set y=2 where y=1;
已更新 1 行。 session2:
SQL> update tab set x=3 where y>0;
aaaaa
aaaaa
这里update语句再次重新执行。好,我们总结如下: 如果在update(delete没有测试,估计应该是相同的)语句的执行过程中,待操作记录发生了改变,并且被改变的列在where子句或者row触发器(after or before)的内部使用:old或者:new引用过,那么update语句会将之前的操作回滚并重新执行。这也是after触发器效率高于before触发器的一个原因,因为即便update重新执行,after触发器只执行一次,而befer触发器会执行两次。如果发生了restart,oracle会在第二次执行的过程中尝试使用select for update来进行操作,以避免发生再一次的restart。但是即便使用select for update ,依然有可能发生restart。
下面看一下restart产生的影响: 1:如果触发器维护plsql中的全局变量,那么全局变量可能是不准确的 2:任何utl_函数,例如UTL_FILE, UTL_HTTP, UTL_SMTP,可能会执行两次,因此这些函数时非事务性的,无法回滚 3:自治事务当然也会受影响而执行多次
|