Oracle 语句重启动

Oracle在执行DML语句时,会用到两种不同的方式去读取数据块:
1. 一致读:在“找到”需要修改的数据行时,会采用consistent read
2. 当前读:在“获取”数据块来实际更新数据行时,会采用current read

 

一、重启动发生的原因或场景:

        where条件读取时采用一致读和更新时采取当前读版本不一致时就会发生重启动。

如:update test set x=1 where y=1;
首先Oracle会利用一致读找到所有y=1的数据行,因此就算读取期间有别的会话修改了某一行的y值(如从y=1变为 y=2),Oracle也会一致读(利用undo数据将该行的y列恢复到读取的那个时刻的值(即y=1)),所以这一行还是会被读取。然后,当要实际更新这一行的时候,Oracle会利用当前读去获取数据块的最新版本,由于被其他会话提交过了,所以读到的y值是2,与之前一致读中的y=1不符。所以Oracle就知道了需要 “重启动”这个update操作。

 

二、重启动的流程:

        1.回滚之前的修改操作

        2.进入select for update模式,锁定需要修改的行

        3.对锁定的行进行修改操作
               (这个流程是指在Oracle默认的READ COMMITED隔离级别下,如果是SERIALIZABLE模式,则会出现ORA-08177错误)

 

三、重启动的测试(通过创建触发器):

create table t (x int,y int);
insert into t values(1,1);commit;
create or replace trigger t_buffer before update on t for each row
begin
 dbms_output.put_line ('old.x='||:old.x||',old.y='||:old.y);
 dbms_output.put_line ('new.x='||:old.x||',new.y='||:old.y);
end;

update t set x=x+1 where x>0;
输出结果如下:
old.x=1,old.y=1
new.x=1,new.y=1

在第二个会话中执行
update t set x=x+1 where x>0; --被锁定
将第一个会话中提交后,输出结果如下:
old.x=1,old.y=1
new.x=1,new.y=1
old.x=2,old.y=1
new.x=2,new.y=1

where条件改成y=0也一样 在第一个会话中执行如下语句
update t set x=x+1 where y>0;
old.x=3,old.y=1
new.x=3,new.y=1

在第二个会话中执行
update t set x=x+1 where y>0;
old.x=3,old.y=1
new.x=3,new.y=1
old.x=4,old.y=1
new.x=4,new.y=1

修改触发器如下:
create or replace trigger t_buffer before update on t for each row
begin
 dbms_output.put_line ('已更新');
end;

第一个会话
update t set x=x+1 where x>0;
输出结果:
已更新

第二个会话
update t set x=x+1 where x>0;
输出结果:
已更新
已更新

如果where条件改成y>0,那么就不会引发重启动
第一个会话
update t set x=x+1 where y>0;
输出结果:
已更新

第二个会话
update t set x=x+1 where y>0;
输出结果:
已更新

 

四、为什么需要重启动:
   首先应该注意到“我们的触发器触发了两次!“表中只有一行,而且只有一个BEFORE FOR EACH ROW触发器。我们更新了一行,但触发器却触发了两次。想想看这会有什么潜在的影响。如果你有一个触发器会做一些非事务性的事情,这可能就是一个相当严重的问题。例如,考虑这样一个触发器,它要发出一个更新(电子邮件),电子邮件的正文是“这是数据以前的样子,它已经修改成现在这个样子“。如果从触发器直接发送这个电子邮件,(在Oracle9i 中使用UTL_SMTP,或者在Oracle 10g 及以上版本中使用UTL_MAIL),用户就会收到两个电子邮件,而且其中一个报告的更新从未实际发生过。如果在触发器中做任何非事务性的工作,就会受到重启动的影响。考虑以下影响:
考虑一个触发器,它维护着一些PL/SQL 全局变量,如所处理的个数。重启动的语句回滚时,对PL/SQL 变量的修改不会“回滚“。
一般认为,以UTL_开头的几乎所有函数(UTL_FILE、UTL_HTTP、UTL_SMTP 等)都会受到语句重启动的影响。语句重启动时,UTL_FILE 不会“取消“对所写文件的写操作。作为自治事务一部分的触发器肯定会受到影响。语句重启动并回滚时,自治事务无法回滚。所有这些后果都要小心处理,要想到对于每一个触发器可能会触发多次,或者甚至对根本未被语句更新的行也会触发。之所以要当心可能的重启动,还有一个原因,这与性能有关。我们一直在使用单行的例子,但是如果开始一个很大的批更新,而且它处理了前100,000 条记录后重启动了会怎么样?它会回滚前100,000 行的修改,以SELECT FOR UPDATE 模式重启动,在此之后再完成那100,000 行的修改。

 

注:整理自网络

 



 

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值