在《ORA-1555错误解决一例》一文中,当时尝试模拟UNDO段头事务表被覆盖的情况下出现ORA-01555错误,没有成功。实际上没有成功的原因是事务数虽然多,但是事务过小,使UNDO块没有被覆盖完,这样通过回滚事务表仍然能够得到事务表以前的数据。本文进一步讨论一些有关延迟块清除和一致性读方面的内容(但不会涉及到延迟块清除和一致性读的具体概念和过程,只是一些有趣的东西)。
先来看看一个数据块中ITL的转储:
Block header dump: 0x0200410a Object id on Block? Y seg/obj: 0x5f07 csc: 0xb08.1303a672 itc: 3 flg: - typ: 1 - DATA fsl: 0 fnx: 0x0 ver: 0x01 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0001.025.0000472d 0x00000000.0000.00 C--- 0 scn 0x0b08.12f461da 0x02 0x0007.015.00004ba0 0x0080d9a0.16f7.39 C-U- 0 scn 0x0b08.12fb5cae 0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
我们看看ITL中的第2个条目,其Flag为"C-U-",C表示commit(提交),U表示Upper bound(上限),这里”U“表明ITL中记录的事务的提交SCN并不是精确的,只是表明事务提交的精确SCN是在ITL中记录的SCN之前,对于第2条ITL来说,其事务提交SCN小于或等于”0x0b08.12fb5cae“。那么这里的问题是:Upper bound是在什么情况下出现的?如果一个SQL语句对该块进行一致性读时,发现ITL中的Upper bound的SCN比一致性读需要的SCN大,这时会发生什么?要回答这些问题,先来看下面的一系列测试过程:
1. 在会话1中建测试表t1,将插入500行数据,每个块只有1行数据,一共500个块,然后再创建一个较大的测试表t2,插入1000行数据:
SQL> @mysid SID ---------- 160 SQL> create table t1 ( id number, small_vc varchar2(20),padding varchar2(1000)) pctfree 90 pctused 10; 表已创建。 SQL> insert /*+ append */ into t1 2 select rownum,rownum || lpad('0',10,'0'),lpad('0',1000,'0') 3 from dba_objects 4 where rownum< =500; 已创建500行。 SQL> SQL> commit; 提交完成。 SQL> create table t2 (id number,vc varchar2(20),padding varchar2(1000)) pctfree 90 pctused 10; 表已创建。 SQL> insert /*+ append */ into t2 2 select rownum,lpad(rownum,20,'0'),lpad(rownum,1000,'0') 3 from dba_objects 4 where rownum<=1000; 已创建1000行。 SQL> commit; 提交完成。 SQL> select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid) 2 from t1 3 where rownum<=5; DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) ------------------------------------ ------------------------------------ 8 16650 8 16651 8 16652 8 16653 8 16654
2. 在会话1中更新测试表T1中的所有行,并获取事务ID,然后再dump1个数据块和事务对应的UNDO段头块:
SQL> update t1 set padding=lower(padding); 已更新500行。 SQL> select xidusn,xidslot,xidsqn,to_char(start_scnw,'xxxxxxxx') start_scnw, 2 to_char(start_scnb,'xxxxxxxx') start_scnb, 3 start_scnb+start_scnw*power(2,32) start_scn from v$transaction; XIDUSN XIDSLOT XIDSQN START_SCN START_SCN START_SCN ---------- ---------- ---------- --------- --------- ----------------- 7 21 19360 b08 12f461db 12129305649627 SQL> select file_id,block_id from dba_rollback_segs where segment_name='_SYSSMU7$'; FILE_ID BLOCK_ID ---------- ---------- 2 105 SQL> alter system dump datafile 8 block 16650; 系统已更改。 SQL> alter system dump datafile 2 block 105; 系统已更改。
事务使用的事务表在回滚段_SYSSMU7$上,即第7个回滚段。事务表中的条目为21,即事务表中的第21条记录。
数据块dump出来的结果是(去掉了对本文话题无关紧要的内容,以后也是如此):
*** 2012-05-26 11:14:38.439 Start dump data blocks tsn: 8 file#: 8 minblk 16650 maxblk 16650 buffer tsn: 8 rdba: 0x0200410a (8/16650) scn: 0x0b08.12f461f5 seq: 0x01 flg: 0x00 tail: 0x61f50601 frmt: 0x02 chkval: 0x0000 type: 0x06=trans data Hex dump of block: st=0, typ_found=1 Block header dump: 0x0200410a Object id on Block? Y seg/obj: 0x5f07 csc: 0xb08.12f461ce itc: 3 flg: - typ: 1 - DATA fsl: 0 fnx: 0x0 ver: 0x01 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0001.025.0000472d 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000 0x02 0x0007.015.00004ba0 0x0080d9a0.16f7.39 ---- 1 fsc 0x0000.00000000 0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
可以看到ITL中的第2条正是当前活动事务在这个块上所使用的ITL。Xid为“0x0007.015.00004ba0”,转换成10进制正是“7.21.19360”,这跟之前查询出来的事务ID是一致的。ITL中此时的flag为"----",正是活动事务的标志。由于块中只有1行数据,因此Lck为1,即该事务在这个块中锁住的行数为1行。
下面再来看看此时UNDO段头块的转储结果: Read the rest of this entry