DML重启,这是我今天学到的一个重要概念,总结下:
DML操作时,会用到2种读:一致读和当前读,一致读用于更新前去找DML需要操作那些数据,当前读用于更新到当前数据时,看数据的最新版本,如果当前读与一致读看到的数据
不一致的话,就会发生DML的重启。重启DML即将DML影响的数据全部回滚(只针对DML操作的数据,事务中的其他DML操作不会受影响),重新再进行一次同样的DML。
DML重启一般是检测不到的,不过可以用触发器来监测,确实有这种现象
1. 建表
CREATE TABLE t (x INT,y INT);
2.插入数据
INSERT INTO t (x,y) VALUES (1,1);
3.创建t表update前的触发器
CREATE OR REPLACE TRIGGER t_tr
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 = '||:new.x ||'new.y = '||:new.y);
END;
4.session1 开始update
UPDATE t SET x=x+1 WHERE x>0;
并不提交,看下output
old.x = 1old.y = 1
new.x = 2new.y = 1
5.session2开始同样的update
UPDATE t SET x=x+1 WHERE x>0;
此时session2被session1阻塞,session1 提交,来看下session2的output
old.x = 1old.y = 1
new.x = 2new.y = 1
old.x = 2old.y = 1
new.x = 3new.y = 1
这里可以清楚的看到触发器被触发了2次,原因就是第一次当前读(x=2),与一致读(x=1)不一致,造成了update操作的重启!!!
这里大家就要注意两个问题:
1. 如果你用类似上面的触发器来控制程序的话,可能会发生你意想不到的结果,例如我本想只触发一次,结果不知道为什么触发了多次;
2.执行大批量数据的DML操作时,可能前面已经更新了10W行了,到下一行更新时,当前读与一致读不一致,结果前面的10W行更新将会
全部回滚!!这也许可以解释,为什么明明应该很快的DML操作有时候需要几倍的时间