本帖最后由 13811135929 于 2016-1-13 16:19 编辑
结合《oracle核心技术》(JL)、网上的资料以及自己的理解,对于事务表在测试环境中做了如下实验,如有错误,还请大师指点数据库版本信息如下:
SCOTT>select * from v$version;
BANNER
------------------------------------------------------------------------------------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
oracle开始事务前,根据现有undo段的情况选择一个undo段,此处假设选择了_SYSSMU5$
事务表控制部分:
session2:alter system dump undo header '_SYSSMU5$';
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(9).png
1.PNG (70.93 KB, 下载次数: 13)
2016-1-12 15:31 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(11).png
一般情况每个undo段头会有0x2f个事务表槽(oracle版本不同,事务表槽的个数可能不同),一个事务会占用其中一个事务表槽,undo段头事务表会自动维护一张链表,oracle根据链表的顺序对事务表进行重用。
注意上图中chd以及ctl两个部分,其中chd指向了下一个将被使用的事务槽,ctl为事务槽链表的最后一个节点;而0x0015的事务槽uel字段指向了链表的下一个节点(0x0018).因此现在链表的情况如下:
0x0015->0x0018->0x0006->.......->0x000d
session1:update t set OBJECT_NAME='a' where OBJECT_ID=2;
session2:alter system dump undo header '_SYSSMU5$';
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(4).png
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(5).png
2.PNG (70.98 KB, 下载次数: 4)
2016-1-12 15:31 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(6).png
发起新事务后,再次转存_SYSSMU5$段头后,观察事务槽的情况发现新事务确实占用了0x0015,同时chd变为了0x0018,因此现在链表的情况如下:
0x0018->0x0006->.....->0x000d
session1:commit;
session2:alter system dump undo header '_SYSSMU5$';
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(7).png
3.PNG (55.04 KB, 下载次数: 5)
2016-1-12 15:32 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(8).png
当事务提交后,再次转存_SYSSMU5$段头后,发现ctl更换为了刚刚被占用的事务槽0x0015,因此现在链表的情况如下:
0x0018->0x0006->.....->0x000d->0x0015
通过以上的内容可以非常清晰的看出了chd以及ctl的作用:即chd(chain head)和ctl(chain tail)分别为事务表链表的头和尾,每个事务表槽又通过uel字段指向了链表的下一个,从而形成了一个有序的链表。有了此链表,可以让新事务尽快的找到对应的事务表槽并进行占用。
以下再对TRN CTL其他字段进行分析
session2:alter system dump undo header '_SYSSMU5$';
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(14).png
4.PNG (54.79 KB, 下载次数: 3)
2016-1-12 15:33 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(18).png
session1:select dbms_flashback.get_system_change_number from dual;
输出结果:734647
update t set OBJECT_NAME='b' where OBJECT_ID=2;
session2:alter system dump undo header '_SYSSMU5$';
5.PNG (51.76 KB, 下载次数: 9)
2016-1-12 15:34 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(15).png
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(19).png
请注意TRN CTL的uba和scn字段:
uba:0x00800068.0179.03->0x00800068.0179.04
scn:0x0000.000b1b69->0x0000.000b1b73
0x0000.000b1b73:此scn为事务表槽被占用之前的scn,即上次用此事务表槽的事务的提交scn.还有一点该scn为_SYSSMU5$段中所有事务表中已知的最小的scn.
0x00800068.0179.04 此uba的地址代表什么?让我们来查询v$transaction视图进行确认
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(20).png
6.PNG (65.61 KB, 下载次数: 3)
2016-1-12 15:35 上传
从v$transaction的输出可初步判断:0x00800068.0179.04应该为新事务undo的第一个记录.那么确认这个猜想呢?也许你会说我们的dml语句只占用了一个undo记录,你凭什么判断该记录就是事务的第一个undo记录呢?
session1:update t set OBJECT_NAME='b'
执行了上述语句后,再去查看v$transaction,通过USED_UBLK字段可以判断事务使用了1021个undo block,且第一个undo block为104(START_UBABLK), 最后的block为1697(UBABLK).
7.PNG (66.18 KB, 下载次数: 5)
2016-1-12 15:36 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(21).png
那我们再次转存_SYSSMU5$信息
session2:alter system dump undo header '_SYSSMU5$';
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(16).png
8.PNG (53.1 KB, 下载次数: 3)
2016-1-12 15:37 上传
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(17).png
虽然事务又占用了大量的undo block,细心的你可能发现TRN CTL部分并没有发生任何变化,因此可以进一步断定uba的确为新事务的第一个undo记录.你可能还会发现事务表槽对应的dba由0x00800068变成了0x008006a1.对0x0080061计算后发现,其地址为datafile=2 block=1697,发现了什么?对,事务表槽对应的dba记录的是当前事务占用的最后一个undo block地址。
上面对整个事务控制部分主要字段进行了详细的介绍,下面我们再深入的研究一下undo block的内容
对0x00800068.0179.04进行转存
session2:alter system dump datafile 2 block 104;
file:///C:/Users/icbc/AppData/Local/Temp/enhtmlclip/Image(22).png
9.PNG (169.26 KB, 下载次数: 6)
2016-1-12 15:39 上传
可以看到uba为上一个被占用的事务表槽的第一个undo记录(注意,这个uba肯定不是slt:0x10,而是别的slot),而prv tx scn为覆盖slt:0x10之前事务表槽的提交scn,prev brb为覆盖slt:0x10之前事务表槽的事务的最后一个undo块,txn start scn:scn为覆盖slt:0x10之前事务的开始scn.
ok,让我们来再次屡屡事务表的机制,假设新建数据库,事务表未使用过。
1.第一个事务A用了slot:0x0010,A开始后TRN CTL的uba记录了A的第一个undo记录dba-A,同时也记录了slot:0x0010之前的状态 (包括提交scn,undo的最后一个块)。
2.隔了一段时间,该段的第二个事务B使用了该段,根据chd的标记占用了slot:0x000a,B开始后TRN CTL的uba更改为B的第一个undo记录dba-B,另外dba-B中记录了dba-A的地址和slot:0x000a之前的状态(包括提交scn,undo的最后一个块)
那么问题来了?怎么回退事务表呢?
1.oracle找到TRN CTL的uba dba-B(从上面的描述可以知道此为最新事务的第一个undo记录),从该uba的第一行可以得知最新事务占用的slot:0x00a,且原来占用slot的主要信息也进行了记录:wrap#-1,uel更改为现在TRN CTL的chd,scn更改为prv tx scn,dba更改为prev brb.
2.如果回退一步不满足要求,那么则再进行回退,即回退dba-B中记录的上一个TRN CTL的uba地址dba-a,从该uba的第一行也可得知次新事务占用的slot:0x0010,且原来占用slot的主要信息也进行了记录:wrap#-1,uel更改为0x000a,scn更改为prv tx scn,dba更改为prev brb.
.........
经过上述的描述,可以简单的概括为:通过事务的第一个undo记录链表完成了事务表的回退,为块延迟清除找到准确的提交scn提供了强有力的保证。