[English]
作者:
fuyuncat
来源:
www.HelloDBA.com
快速提交
当一个事物访问一个数据块时,它会占用数据块Itl中的一个条目,记录下事务ID(Xid)、该事务对该数据块操作时使用的回滚块地址(Uba)和在该数据块上产生的行级锁的数量,并在对应数据行上打上行锁标志,与Itl对应。当提交时,设置Itl中的事物标志为U,并写入一个快速提交SCN(Fsc),但并不清除锁标志。这就是快速提交。
看下面的一个试验。
先对数据块进行修改,然后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> commit;
Commit complete.
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> alter system dump datafile 5 block 50959;
System altered.
看看dump出来的内容:
Block header dump: 0x0140c70f
Object id on Block? Y
seg/obj: 0xe46c csc: 0x00.a4a48c25 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 0x000f.006.00000a14 0x02c00050.0376.10 C--- 0 scn 0x0000.a4a39ef0
0x02 0x0013.003.00000a58 0x02c0008f.039a.0a --U- 1 fsc 0x0000.a4a48c26
... ...
block_row_dump:
tab 0, row 0, @0x1f5e
tl: 10 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 02
col 1: [ 3] c2 02 10
tab 0, row 1, @0x1f4a
tl: 10 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [ 3] c2 03 17
... ...
可以看到,事务在Itl中记录下了Xid、Uba、Flag(U)、锁、Fsc;并且记录上的锁标志没有清除。
延迟块清除
从上面内容可以知道,事务在数据块上作的操作并没有清除产生的锁标志和其他标志(检测锁冲突),这些标志的清除需要留到下一个访问该数据块的事务去做清除。下一个访问到该数据块的事务会先检查自己要访问的数据行上有没有锁,如果都没有,就继续作自己的操作,如果记录上有锁,就做一次清除(Cleanout)。看下面的试验:
这个试验是接着上面的,但是这修改的是数据块中的另外一条记录:
SQL> conn demo/demo
Connected.
SQL>
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> update t_multiver set b=115 where a=2;
1 row updated.
SQL>
SQL> commit;
Commit complete.
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> alter system dump datafile 5 block 50959;
System altered.
看看dump出的数据块:
Block header dump: 0x0140c70f
Object id on Block? Y
seg/obj: 0xe46c csc: 0x00.a4a48c25 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.008.000007f2 0x02c0003f.02ca.24 --U- 1 fsc 0x0000.a4a48df3
0x02 0x0013.003.00000a58 0x02c0008f.039a.0a --U- 1 fsc 0x0000.a4a48c26
... ...
block_row_dump:
tab 0, row 0, @0x1f5e
tl: 10 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 02
col 1: [ 3] c2 02 10
tab 0, row 1, @0x1f4a
tl: 10 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [ 3] c2 02 10
tab 0, row 2, @0x1f54
... ...
这个事务并没有清除上一事务的锁标志,并且还产生了自己的锁标志,因为他们之间没有锁冲突。从这个试验,我们还可以得出以下结论:事务是按照数据块中的记录处理的,不是按照Itl中的条目处理的。
而块清除可以分为两种情况。一种是针对快速提交的。如果快速提交后,脏数据回写到磁盘上,此时数据块的锁标志还没有清除,后面访问该数据块的事务(只是产生数据更新的事务,不包括读事务),会先清除(Cleanout)上一个快速提交事务留下的锁标志(同时也是做锁判断),然后开始事务操作。看看试验:
这里继续接着上面的试验,再对数据块做一次修改操作,并且修改的记录上已经有锁标志,看看后面事务是否清除了前面事务的标志:
SQL> conn demo/demo
Connected.
SQL>
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> update t_multiver set b=115 where a=2;
1 row updated.
SQL>
SQL> commit;
Commit complete.
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> alter system dump datafile 5 block 50959;
System altered.
看看dump出的内容:
Block header dump: 0x0140c70f
Object id on Block? Y
seg/obj: 0xe46c csc: 0x00.a4a4918e 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.008.000007f2 0x02c0003f.02ca.24 C--- 0 scn 0x0000.a4a48df3
0x02 0x0014.007.000007a2 0x02c000a5.02ab.23 --U- 1 fsc 0x0000.a4a4918f
... ...
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
tl: 10 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 03
col 1: [ 3] c2 02 10
tab 0, row 2, @0x1f54
... ...
可以看到,这个事务对前面的事务作了一次Cleanout,所有相关标志都被修改。
另一种情况是,事务开始后,会给数据块打上锁标志。但是事务标志和SCN号是在事务提交后写入的,如果在事务提交前,DBWn进程已经将数据回写到磁盘,那么数据块上就没有这些信息。后面访问该数据块的事务(包括所有读写事务)需要先根据Xid和Uba从回滚段的事务信息表中获取到这些信息,然后作一次Cleanout。再看试验:
事务提交前回写数据块:
SQL> update t_multiver set b=115 where a=1;
1 row updated.
SQL>
SQL> alter system flush buffer_cache;
System altered.
SQL>
SQL> commit;
Commit 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.a4a49c85 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 ---- 1 fsc 0x0000.00000000
0x02 0x000d.028.00000a2b 0x02c00035.039b.03 C--- 0 scn 0x0000.a4a49c71
... ...
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
... ...
可以看到,Flag和scn没有被记录,而Xid、Uba和锁标志都已经被记录。这里还可以推断出:Xid和Uba在事务开始,获取到空闲Itl后是最先被记录的;锁标志是针对记录一一被记录的;Flag和Scn是事务提交后被记录的。
看看这种情况下,是如何做Cleanout的。我们这不再选用更新事务,而是做一个读操作:
SQL> alter system dump datafile 5 block 50959;
System altered.
SQL> select * from t_multiver;
A B
---------- ----------
1 115
2 115
3 222
SQL>
SQL> alter system dump datafile 5 block 50959;
System altered.
看Dump内容:
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 0x000d.028.00000a2b 0x02c00035.039b.03 C--- 0 scn 0x0000.a4a49c71
... ...
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
... ...
可以看到,前面事务的标志都被Cleanout了。