上述dump中uba: 0x02000010.187e.18 ctl max scn: 0x0000.00ae9095正是来自于事务开始前undo header里TRN CTL部分所包含的uba: 0x02000010.187e.18 scn: 0x0000.00ae9095,而prv tx scn:0x0000.00ae909b 和prev brb:33554460 则分别等于事务开始前slot 0x1e 所对应的scn:00ae909b和dba:0x0200001c(转换成10进制就是33554460)。只有事务的首条undo记录才会保留上一版本的undo信息。将相关的undo记录串起来就可以完整的回放事务表所发生过的变更,下面较为直观的方式说明一下undo 记录间的指向关系,还是以proc1.sql里前三个事务为例,它们所使用的xid,uba如下:
XIDUSN,XIDSLOT,XIDSQN,UBAFIL,UBABLK,UBAREC,UBASQN : 2,31,4563,8,20,27,6270
XIDUSN,XIDSLOT,XIDSQN,UBAFIL,UBABLK,UBAREC,UBASQN : 2,2,4565,8,20,28,6270
XIDUSN,XIDSLOT,XIDSQN,UBAFIL,UBABLK,UBAREC,UBASQN : 2,22,4564,8,20,29,6270
Undo record的记录是后修改的指向先修改的,所以依次dump block 8/20中 Rec #0x1d、Rec #0x1c、 Rec #0x1b的记录,仅将相关的内容摘录下来:
*-----------------------------
* Rec #0x1d slt: 0x16 objn: 89731(0x00015e83) objd: 89731 tblspc: 8(0x00000008)
* Layer: 11 (Row) opc: 1 rci 0x00
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x02000014.187e.1c ctl max scn: 0x0000.00ae92ed prv tx scn: 0x0000.00ae92ee
txn start scn: scn: 0x0000.00ae9357 logon user: 0
prev brb: 33554451 prev bcl: 0
*-----------------------------
* Rec #0x1c slt: 0x02 objn: 89731(0x00015e83) objd: 89731 tblspc: 8(0x00000008)
* Layer: 11 (Row) opc: 1 rci 0x00
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x02000014.187e.1b ctl max scn: 0x0000.00ae92ec prv tx scn: 0x0000.00ae92ed
txn start scn: scn: 0x0000.00ae9354 logon user: 0
prev brb: 33554451 prev bcl: 0
*-----------------------------
* Rec #0x1b slt: 0x1f objn: 89731(0x00015e83) objd: 89731 tblspc: 8(0x00000008)
* Layer: 11 (Row) opc: 1 rci 0x00
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x02000014.187e.18 ctl max scn: 0x0000.00ae92eb prv tx scn: 0x0000.00ae92ec
txn start scn: scn: 0x0000.00000000 logon user: 0
prev brb: 33554451 prev bcl: 0
通过这三个undo Record能够映射得到前一时刻的undo事务表,假设T3>T2>T1>T0,以下图示能够直观的展现这一映射关系:
上面的图能够帮助我们理解oracle是如何实现事务表的回滚,只要undo block不被覆盖,就能回滚得到足够旧的事务表。
具备了上述知识点后,我们分析一下session 2是如何成功遍历scott.tabnow1表的:
(1) Session 2的select语句执行时的snapshot scn近似等于ae931d
(2) 访问rdba:4/1779 时发现Itl列表里slot 0x02还处于活动状态,且没有commit scn,于是就到xid所指向的undo 事务表进一步查询事务是否提交,slot 0x1e的state=9表示事务已经提交,但提交时的wrap#值为0x11dc远大于0x11d2,且undo 事务表头的control scn:ae96dc远大于snapshot scn: ae931d
(3) Oracle判断事务表槽已slot 0x1e已经被覆盖,需要回滚事务表,回滚的起点是8/14 Rec #0x21,这个Record是proc1.sql中第337个transaction所使用的undo record,因为正是这个transaction完成了对undo slot 0x1e的最后一次覆盖
(4) 接下来将如上图所示,oracle一步一步的对事务表进行回滚,每重构出一个事务表的版本后,将事务表头部的control scn和snapshot scn进行比较,如果snapshot scn<control scn则继续进行回滚,直到snapshot scn>=control scn而且wrap#的值等于0x11d2,事务表才回滚完毕,从undo record :8/16 Rec #0x19里读出修改前的值:
select utl_raw.cast_to_varchar2(replace('58 53 24 4e 55 4c 4c 4c 4c',' ')) from dual;
UTL_RAW.CAST_TO_VARCHAR2(REPLACE('5853244E554C4C4C4C',''))
--------------------------------------------------------------------------------
XS$NULLLL
Session 2实现一致性读的两个统计信息: