一、回滚段块的使用总结:
1.1 Seq序列值:
步骤1:
在每个回滚块的块头,有一个SEQ值,当事务延伸到新区时,新区的SEQ是上一区SEQ加1。如:
www.2cto.com
步骤2:
步骤3:事务B提交
步骤4:事务A发布新的更新声明,它所占回滚块开始增长:
事务A增长,占用83、84、85、86、87块,但到达88块时,发现88、89块的序列值也是10,并不小于自身的序列值,事务A跳过88、89块,90块的序列值小于10,事务A占用90号块。
步骤5:
事务A继续增长,当事务A延伸至下一区时,下一区块的序列值为11:
结论:只有当事务为块分配的序列值大于块本身的序列值时,事务才可占用此块。上例中事务 B已提交的块,只有当A事务提交后,另有其他事务从末尾区延伸到区0时,才会被占用。
www.2cto.com
1.2 区的膨胀(Extend):
事务A不断增长,已经占满了区0。事务A又请求新的回滚块,由于事务Z从区1开始,因此区1有“活动事务”,此时
Oracle再分配一个新区作为区1,老的区1变为区2,如下:
就算老区1后还有空闲的老区2,此处还是会分配新区1的。
二、回滚和提交:
2.1 回滚:
如果某一事务回滚,其操作步骤是:
1.在事务表中取出它的DBA列,此DBA是事务回滚链末端回滚块的DBA
2.通过此DBA找到回滚链的末端
3.沿回滚链从尾向前,依次取出回滚记录所对应的原表的DBA、行号、列号,以及其前映像信息,
4.将前映像信息重新写入原表相应的DBA中
从上面的步骤可以看到,回滚的顺序,与你操作的顺序正相反,如果你删除了1,2,3,4,5条记录,然后回滚,Oracle从后向前,依次插入5,4,3,2,1条记录。
2.2 提交:
事务提交后,事务表上的事务状态列被置为非活动。此事务不可再回滚,因为回滚先要到事务表查出事务回滚链的末尾块。
但是,提交后,虽然事务表上已经标明此事务是非活动事务,不过回滚块中的信息还在,虽然不能回滚,但这些回滚块中的信息在被覆盖前,仍是可以被读取的。
关于这一点,我们可以找个事务,提交后再DUMP它占用的回滚块,根据回滚段循环使用原则,这些回滚记录到下一轮循环时才会被覆盖。
三、自己动手构造CR块:
3.1 查询魔术:
步骤1:在会话17中发布如下声明:
17> var x refcursor
17> exec open :x 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
AA1
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 快照太老错误。
执行时间过长的查询,有时也会报出这个错误。
3.2 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。
3.3 构造CR块:
Oracle会在三种情况下去读回滚段构造CR块:
1.只要数据块上有锁,Oracle将会构造CR块。
2.游标的SCN小于块的SCN,证明块在游标打开之后又被修改过了,这就要去构造CR块。
3.闪回查询中,要求的SCN小于块的SCN,也要去构造CR块。
步骤1:先观察表T8的行分布情况:
ROWIDBLOCK# CID
------------------ ---------- ---------- ----------
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 j10
步骤2:发布更新命令
13> update t8 set c=upper(c) where id<=2;
已更新2行。
13> 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->aaa1aaaa1->aaaaa10->a