免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3365 | 回复: 2
打印 上一主题 下一主题

晶晶实验六自己动手构造CR块 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-27 09:17 |只看该作者 |倒序浏览
晶晶实验六自己动手构造CR块


一、查询魔术
步1:在会话17中发布如下声明:
17> var x refcursor
17> exec open for select substr(c,1,5),id from t8;
PL/SQL 过程已成功完成。
步2:在会话13删除T8的所有记录且提交:
13> delete t8 ;
已删除 10 行。
13> commit;
提交完成。
步3:在会话17中输出游标X的所有行:
17> print x
SUBSTR(C,1         ID
---------- ----------
g                   7
h                   8
i                   9
J                  10
AA                  1
B                   2
C                   3
D                   4
E                   5
f                   6
已选择10行。
我们在会话13中已删除且已提交,但在会话17中还是显示出了T8中的所有行,这是为什么呢?
1) 在步1打开游标,但此时并没有执行、执取,在会话17的PGA中,将会开辟一块内存存贮此游标。在游标的相关信息中,有一条就是游标打开是的SCN,假若此

时的SCN是1000。
2) 在步2中对表T8进行了操作,并提交。Oracle每个数据块的头部,都记有此块当前的SCN值,此SCN值随着对块的更新而变化。假如,我们在步2更新T8表时,

SCN是1200,这个值将被记入块头部的SCN值。
3) 在步3我们发布Print x声明,此声明将完成执行、抓取等步骤。在从T8表的相关块中抓取行时,Oracle会将打开游标时的SCN和块本身的SCN相比较,如果发现

后者大于前者,证明打开游标是在更新表之前进行的,Oracle将会在回滚段中寻找小于游标SCN的信息,构造一个在打开游标之前的块,这就是CR块了。如果回滚

段中的T8表的信息已经被覆盖,将会报告一个ORA-01555 快照太老错误。
        执行时间过长的查询,有时也会报出这个错误。
二、SCN
        SCN就是Oracle内部的一个记时机值,也就是Oracle的内部时钟。在9i之后,我们可以通过如下的方式获得当前的SCN号:
scott@MYTWO>
select scn,to_char(scn,’xxxxxxx’) from (select dbms_flashback.get_system_change_number SCN from dual);
   SCN   
----------
8727805
在几乎所有的地方,你都能见到SCN。在事务表、回滚块中的回滚记录、数据块、日志文件等等,我们上面曾提到过,在游标中,记录的也有游标打开时的SCN。
(在eygle的深入浅出oracle中有关SCN号更详细的介绍,18页)
三、构造CR块
Oracle会在三种情况下去读回滚段构造CR块:
1.  只要数据块上有锁,Oracle将会构造CR块。
2.  游标的SCN小于块的SCN,证明块在游标打开之后又被修改过了,这就要去构造CR块。
3.  闪回查询中,要求的SCN小于块的SCN,也要去构造CR块。

构造CR块
步1:先观察表T8的行分布情况:
ROWID                  BLOCK# C                  ID
------------------ ---------- ---------- ----------
AAAB3LAAFAAAf/mAAA     131046 a                   1
AAAB3LAAFAAAf/mAAB     131046 b                   2
AAAB3LAAFAAAf/mAAC     131046 c                   3
AAAB3LAAFAAAf/nAAA     131047 d                   4
AAAB3LAAFAAAf/nAAB     131047 e                   5
AAAB3LAAFAAAf/nAAC     131047 f                   6
AAAB3LAAFAAAf/oAAA     131048 g                   7
AAAB3LAAFAAAf/oAAB     131048 h                   8
AAAB3LAAFAAAf/oAAC     131048 i                   9
AAAB3LAAFAAAf/pAAA     131049 j                  10
步2:发布更新命令
13> update t8 set c=upper(c) where id<=2;
已更新2行。
13> commit;
提交完成。
13> update t8 set c='A1' where id<=1;
已更新 1 行。
13> update t8 set c='A2' where id<=2;
已更新2行。
13> update t8 set c='B2' where id<=2;
已更新2行。
13> commit;
提交完成。
换到其他会话,
17> update t8 set c='aa1' where id<=1;
已更新 1 行。
17> update t8 set c='bb2' where id=2;
已更新 1 行。
17> commit;
提交完成。
17> update t8 set c='aaa1' where id<=1;
已更新 1 行。
17> update t8 set c='bbb2' where id=2;
已更新 1 行。
17> update t8 set c='aaaa1' where id<=1;
已更新 1 行。
17> update t8 set c='bbbb2' where id=2;
已更新 1 行。
17> commit;
提交完成。
再换个会话:
10> update t8 set c='aaaaa10' where id=1;
已更新 1 行。
10> update t8 set c='bbbbb10' where id=2;
已更新 1 行。
10> commit;
提交完成。
10> update t8 set c='az' where id=1;
已更新 1 行。
10> update t8 set c='by' where id=2;
已更新 1 行。
此时的SCN是
18> select dbms_flashback.get_system_change_number SCN from dual;
   SCN
----------
8729612
总结一下
行1的变化a->A->A1 A2 B2->aa1->aaa1  aaaa1->aaaaa10->az
行2的变化b->B->B2       ->bb2->bbb2  bbbb2->bbbbb10->by
下面开始构造CR块,首先说一下末提交时CR块的构造:
步3:末提交时CR块的构造:
接着上面的修改,如果我们在会话16发布查询T8表的声明:
16> select * from t8 where id<=3;
由于最后一次修改没有提交,假如此时有其他会话访问行1或行2,就回发生回滚操作,从而构造一个CR块。让我们自己来回滚一下,感受一下Oracle的回滚操作

到底做了什么:
首先DUMP块131046:
alter system dump datafile 5 block 131046;
在事务的头部,有一块这样的结构:
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0018.008.0000001a  0x03800073.000e.02  ----    2  fsc 0x0000.00000000
0x02   0x0019.007.00000020  0x03800053.0037.01  C---    0  scn 0x0000.008533f6
它被叫做ITL,Interested Transaction List,相关事务列表。此表中每一行,按Oracle惯用的说法,又叫一个槽,SLOT。当有事务修改此块中的数据时,或者

说此块中有事务发生时,就在ITL中占用一个槽。下面我们了解一下ITL中的信息。
第一列,ITL编号,共有两个,分别是0x01,0x02,随着此块中事务的增多,槽数量也会增多。
第二列,Xid,事务Xid编号,Xid共分三段,我们在上文中已经讲过,回滚段编号.槽号.序列号。我们来看0x01事务,它占用24号回滚段的第8个槽,此槽的序列

号是26,即24.8.26。
第三列,UBA,回滚块地址。也分三段,分别是回滚块地址,回滚块序列号,回滚链末记录号(即irb信息)。此处0x01事务占用14号文件,115号回滚块,块序列

号是14,回滚链的末记录在115的第2条记录处。
第四列,Flag,标志,共有四位,四位都是“----”,事务末提交,第一位是C,“C---”,事务已提交。
第五列,Lck,事务所修改的行的数量。这些行,都算是被事务锁住的行。
第六列,SCN/Fsc,是事务的SCN信息。0x01事务尚末提交,故此列为0。
通过上面ITL,我们可以了解到,此块中有一个末提交的事务,那么此事务影响了几行呢,或者说此事务修改了几行呢?在下面行信息中,有:
tab 0, row 0, @0xba7
tl: 1009 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [1000]
61 7a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
这其中的lb: 0x1,就是指此行对应事务0x01,或说此行是事务0x01修改的行,或说事务0x01锁住了此行。它现在的值是61 7a,正是“az”。
        如果在其他会话上发布查询,Oracle首先检查ITL,一但发现某块上尚有末提交的事务,根据其UBA,马上开始构造其CR块,而不管会话所检索的行,是

否是事务相关的。
        块131046上有末提交事务:
0x01   0x0018.008.0000001a  0x03800073.000e.02  ----    2  fsc 0x0000.00000000
其UBA是0x03800073.000e.02,即14号文件,115块,序列号14,第2条回滚记录。下面DUMP115块:
alter system dump datafile 14 block 115;
在DUMP文件中找到根据irb: 0x2,找到115块中第2条回滚记录,它对应131046第1行,前映像值为 62 62 62 62 62 31 30 ,即“bbbbb10”。再根据其rci:0x01

,向上找到第一条回滚记录,它对应131046第0行,前映像值为 61 61 61 61 61 31 30,即“aaaaa10”。
131046块中当前值是:
Slot 0(行1):61 7a         (“az”)          相关末提交事务0x1
Slot 1(行2):62 79         (“by”)         相关末提交事务0x1
Slot 2(行3):63          (“c”)                 无相关事务
通过读事务0x1的回滚块,将前映像信息写进131046,替换事务0x1对应的行,在内存中生成一个新的块:
Slot 0(行1):61 61 61 61 61 31 30         (“aaaaa10”)         相关末提交事务0x1
Slot 1(行2):62 62 62 62 62 31 30         (“bbbbb10”)        相关末提交事务0x1
Slot 2(行3):63                                  (“c”)                        无相关事务
这个新生成的块,块的编号仍是131046,在内存中,它的HASH位置会和原来的131046处于同一HASH Cache Buffer Chain。它就是131046的CR块。CR块的构造到现

在还没有完成,我们用前像映像行值替换了当前的行值,我们最后一步所做的更新并仅仅修改了行的值,上面我们讲到,每个块头有一个ITL,它初始有两个槽,

每个末提交的事务,将占用一个槽,可以随事务增多而扩充。如果事务已提交,它占用的槽会被下一个事务覆盖。因此,我们最后在会话10中做的更新,将覆盖

131046的一个已提事务的ITL槽,这个被覆盖的ITL槽被记进事务回滚链头部块中,如下所示:
op: L  itl: xid:  0x001a.010.00000011 uba: 0x03800085.000e.01
                      flg: C---    lkc:  0     scn: 0x0000.00853185
我们应该用它替换131046中当前的0x01槽:
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0018.008.0000001a  0x03800073.000e.02  ----    2  fsc 0x0000.00000000
0x02   0x0019.007.00000020  0x03800053.0037.01  C---    0  scn 0x0000.008533f6
替换为:
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x001a.010.00000011  0x03800085.000e.01  C---    0  fsc 0x0000.00853185
0x02   0x0019.007.00000020  0x03800053.0037.01  C---    0  scn 0x0000.008533f6
好了,到现在,我们终于构造了一个131046的CR块:
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x001a.010.00000011  0x03800085.000e.01  C---    0  fsc 0x0000.00853185
0x02   0x0019.007.00000020  0x03800053.0037.01  C---    0  scn 0x0000.008533f6

Slot 0(行1):61 61 61 61 61 31 30         (“aaaaa10”)         相关末提交事务0x1
Slot 1(行2):62 62 62 62 62 31 30         (“bbbbb10”)        相关末提交事务0x1
Slot 2(行3):63                                  (“c”)                        无相关事务
现在,131046的CR块中的ITL中,所有事务都已提交,我们的回滚是否可以到此为止呢?
还不行,Oracle还要对比一下查询游标打开时的SCN,如果大于上面CR块中ITL的最大提交SCN:8533f6,证明查询发生在事务结束之后,查询时的SCN大致是:
18> select scn,to_char(scn,'xxxxxxxxxx') from (select dbms_flashback.get_system_change_number SCN from dual);
SCN                 TO_CHAR(SCN
----------                 -----------
8734649      8547b9
后于CR块ITL中最大提交SCN,好,到此为止,回滚结束。
如果游标的SCN前于ITL中最大提交SCN,Oracle仍要继续回滚。
步4:闪回查询时CR块的构造:
假如用户发布如下声明:
select substr(c,1,5),id from t8 as of scn 8728960 where id<=3;
此声明指定读取SCN为8728960时块中的行。这就是闪回查询。
查询要求SCN:853180,前于步3时构造的CR块,因此,需要继续向前回滚。仍然根据ITL中的UBA信息,取出前映像,来构造另一个CR块。问题时,ITL有两个槽,

我们该从哪个机槽的UBA回滚呢。最好的方法是从SCN最大的哪个。此处是槽0x02:
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x02   0x0019.007.00000020  0x03800053.0037.01  C---    0  scn 0x0000.008533f6
UBA中显示,此事务的回滚链尾块在14号文件,83号块,中第1条记录,回滚块序列号是55。
alter system dump datafile 14 block 83;
打开DUMP文件,首先要检查一下,此回滚块是否已被别的事务覆盖,检查方法,非常简单,看此UnDo块的Seq值:
UNDO BLK:  
xid: 0x0019.008.00000021  seq: 0x37  cnt: 0x11  irb: 0x11  icl: 0x0   flg: 0x0000
仍是0x37,和UBA中所记的一样,证明此回滚块还末被覆盖。取出前映像,对应5号文件131046块的Slot 1(行2)的第0列: 62 62 62 62 32,即“bbbb2”。继

续沿回滚链向上,根据rdba: 0x03800052,回滚链上一块在82块中,继续DUMP:
alter system dump datafile 14 block 82;
打开DUMP文件,检查块的Seq值,仍是37。取出前映像,对应5号文件131046块的Slot 0(行1)0列,值为:61 61 61 61 31,即“aaaa1”。
此回滚记录已是回滚链头,被覆盖的ITL槽是:
op: L  itl: xid:  0x0018.005.0000001a uba: 0x03800072.000e.08
                      flg: C---    lkc:  0     scn: 0x0000.00853171
构造一个新的CR块:
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x001a.010.00000011  0x03800085.000e.01  C---    0  fsc 0x0000.00853185
0x02   0x0018.005.0000001a  0x03800072.000e.08  C---    0  scn 0x0000.00853171
0x02   0x0019.007.00000020  0x03800053.0037.01  C---    0  scn 0x0000.008533f6

Slot 0(行1):61 61 61 61 61 31 30         (“aaaaa10”)         相关末提交事务0x1
Slot 1(行2):62 62 62 62 62 31 30         (“bbbbb10”)        相关末提交事务0x1    --红色字体为应该覆盖的信息
Slot 0(行1):61 61 61 61 31         (“aaaa1”)         
Slot 1(行2):62 62 62 62 32         (“bbbb2”)        
Slot 2(行3):63                                  (“c”)                        
如果你发布如下声明:
select substr(c,1,5),id from t8 as of scn 8728960 where id<=3;
这将会先构造步3时的CR块,再构造此步中的CR块,这两个CR块都是131046的CR块,因此,步4时的CR块会覆盖步3时的CR块。也就是说,在一次查询中,Oracle对

应一个数据块,只会在内存创建一个CR块。
好了,我们的查询到此可以为止了吗,对比SCN,查询SCN:853180,ITL中最大SCN:853185。ITL中最大SCN仍后于查询SCN。继续回滚:
步5:继续回滚:
根据ITL中最大SCN槽的Uba,
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x001a.010.00000011  0x03800085.000e.01  C---    0  fsc 0x0000.00853185
回滚链尾块在14号文件,133回滚块,第1条回滚记录,序列号是14。DUMP133块:
alter system dump datafile 14 block 133;
打开DUMP文件,检查块的Seq值,仍是0xe。取出前映像,对应5号文件131046块的Slot 1(行2)0列,值为:62 62 62 32,即“bbb2”。回滚链上一块在rdba:

0x03800084,132块中:
alter system dump datafile 14 block 132;
找到最后一条回滚记录,它的前映像对应5号文件131046块的Slot 0(行1)0列,值为 61 61 61 31,即“aaa1”,但它并是回滚链头,根据它的Rci  0x02,继

续向上,找到0x02条回滚记录, 它对应对应5号文件131046块的Slot 1(行2)0列,62 62 32,即“bb2”,还没完,再向上,是131046块中Slot 0(行1)0列,

61 61 31,即“aa1”,此条回滚记录的Rci已经是0了,已经到了回滚链头,被覆盖的ITL槽是:
op: L  itl: xid:  0x0017.003.00000045 uba: 0x03800021.0021.03
                      flg: C---    lkc:  0     scn: 0x0000.008530fd
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x001a.010.00000011  0x03800085.000e.01  C---    0  fsc 0x0000.00853185
0x01   0x0017.003.00000045  0x03800021.0021.03  C---    0  fsc 0x0000.008530fd
0x02   0x0018.005.0000001a  0x03800072.000e.08  C---    0  scn 0x0000.00853171

Slot 0(行1):61 61 61 61 31         (“aaaa1”)      
Slot 1(行2):62 62 62 62 32         (“bbbb2”)        
Slot 0(行1):61 61 31         (“aa1”)         
Slot 1(行2):62 62 32         (“bb2”)        
Slot 2(行3):63                  (“c”)               
查询的SCN是853180,ITL中最大SCN是853171,查询SCN大于ITL中的SCN,说明查询发生在事务后,好了,到此,我们这一次CR块构造完毕。此次生成的131046的

CR块会覆盖步4时生成的,一个块,在一次查询中,只会产生一个CR块。
根据我们的操作顺序:
a->A->A1 A2 B2->aa1->aaa1  aaaa1->aaaaa10->az
b->B->B2       ->bb2->bbb2  bbbb2->bbbbb10->by
                     ↑
我们已经回到了箭头所指处。我们可以一直向前回塑,直到T8的行被插入131046时,或者,回滚段被覆盖时。假若我们又向前回塑,但回滚块的Seq和我们要求的

不一样,证明此块被覆盖了,此时Oracle会报告Ora-01555错误。我们只需让回滚段的区足够多,这样它每次绕回的时间必然加长。这样,回滚段必然可更长时间

的保持已提交事务的回滚信息。
在X$BH视图中上,有一个CR_SCN_BAS列,显示CR块的最大SCN:
18> select file# ,DBABLK,state, tch ,cr_scn_bas,to_char(cr_scn_bas,'xxxxxxxx') SCN from x$bh
where file#=5 and dbablk=131046;
     FILE#     DBABLK      STATE      TCH CR_SCN_BAS SCN   
---------- ---------- ---------- ---------- ---------- --------- -------------------- ----------
         5     131046          3          0    8728964    853184
上面的0x853184就是咯。旁边的8728964是他的10进制值.

论坛徽章:
0
2 [报告]
发表于 2008-09-27 15:16 |只看该作者
提个意见啊,晶晶,你的长点的章,最好分一下节,并加上粗体字。。。。。。
不然看起来挺晕的。

论坛徽章:
0
3 [报告]
发表于 2008-10-06 16:56 |只看该作者


有深度,够专业的!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP