[English]
作者:
fuyuncat
来源:
www.HelloDBA.com
回滚
如果事务一旦回滚,则会清除数据块上的所有标志,就不存在延迟块清除了。
还是看看试验吧,回更清楚一些。
先做更新操作,在提交/回滚前dump:
SQL> conn demo/demo
Connected.
SQL>
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> update t_multiver set b=115 where a=1;
1 row updated.
SQL>
SQL> alter system dump datafile 5 block 50959;
System altered.
看看数据块上的变化:
Block header dump: 0x0140c70f
Object id on Block? Y
seg/obj: 0xe46c csc: 0x00.a4a49e6b itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x140c709 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0012.01b.000009ae 0x02c0007c.0372.27 ---- 1 fsc 0x0000.00000000
0x02 0x000c.017.00000a25 0x02c0001f.0396.21 C--- 0 scn 0x0000.a4a4a07b
... ...
block_row_dump:
tab 0, row 0, @0x1f5e
tl: 10 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 02
col 1: [ 3] c2 02 10
tab 0, row 1, @0x1f4a
... ...
注意到Xid、Uba和Lck标志已经被打上,但是Flag和SCN还没有。
然后回滚事务:
SQL> rollback;
Rollback complete.
SQL>
SQL> alter system dump datafile 5 block 50959;
System altered.
再看数据块内容:
Block header dump: 0x0140c70f
Object id on Block? Y
seg/obj: 0xe46c csc: 0x00.a4a49e6b itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x140c709 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x000e.00a.000007f5 0x02c0003f.02cc.1e C--- 0 scn 0x0000.a4a49c89
0x02 0x000c.017.00000a25 0x02c0001f.0396.21 C--- 0 scn 0x0000.a4a4a07b
... ...
block_row_dump:
tab 0, row 0, @0x1f5e
tl: 10 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [ 3] c2 02 10
tab 0, row 1, @0x1f4a
... ...
所有锁标志被清除,Itl都已经被还原(这个测试是接着前面的进行的,可以对比前面dump出来的内容,发现Xid、Uba和Scn是一样的,所以说是“还原”)。
一致性读
当一个读操作读到某一个数据块上,发现该数据块有被修改过的痕迹(事务标志、锁标志),会需要通过SCN来检查是读取当前数据还是修改前的数据,以保证读取到的所有数据都是读操作开始发生那一时刻(快照)的数据来保证数据的一致性。如果数据块上事务的SCN<读操作的SCN,说明数据块上的更改是发生在读操作之前的(所有SCN是唯一的,并且是累加的),就可以直接读取数据块上修改过的数据;否则,就需要从回滚段中读取修改前的数据,以保持数据的一致性。这就是一致性读。
下面是一个一致性读的例子:
SQL> select * from t_multiver;
A B
---------- ----------
1 115
2 115
3 222
SQL> var cc refcursor
SQL>
SQL> begin
2 open :cc for select * from t_multiver;
3 end;
4 /
PL/SQL procedure successfully completed.
SQL>
SQL> update t_multiver set b=115 where a=3;
1 row updated.
SQL> print :cc
A B
---------- ----------
1 115
2 115
3 222
SQL> select * from t_multiver;
A B
---------- ----------
1 115
2 115
3 115
从这个例子可以看到,读取操作只会读取读取查询开始时那一时刻的数据,如果数据在读取操作以后发生了变化,则从回滚段中读取老数据镜像。如果修改事务已经提交,并且在读取操作读到该数据块时,回滚段中的信息已经被覆盖,则会发生1555错误。我另外一篇文章介绍了1555错误,这就不再赘述了。
事务在数据块的微观操作过程
总结以上,我们可以绘制出一个更新事务在数据块上微观操作流程图了。当然,这个流程图并不能反映出所有细节,毕竟Oracle还有很多内部的东西是我们没办法知道的。