undo機制是oracle的巧妙之處,也是學習oracle的痛點之一。
下面我們從內部來具體瞭解它。
會話1:scott使用者,發起一個事務
hr@ORCL> conn scott/oracleConnected.scott@ORCL> UPDATE emp SET sal=4000 WHERE empno=7788;1 row updated.scott@ORCL> SELECT empno,ename,job,mgr,sal FROM emp WHERE empno=7788; EMPNO ENAME JOB MGR SAL---------- ---------- --------- ---------- ---------- 7788 SCOTT ANALYST 7566 4000
先不提交這個事務,會話2:SYS使用者
獲得事務資訊
從v$transaction,可得:該事務在9號復原段,位於26號事務槽
sys@ORCL> SELECT xidusn,xidslot FROM v$transaction; XIDUSN XIDSLOT---------- ---------- 9 26
從v$rollstat,也可見該事務高就9號復原段
sys@ORCL> SELECT usn,xacts FROM v$rollstat; USN XACTS---------- ---------- 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 1 10 0
dump復原段的段頭資訊
sys@ORCL> SELECT * FROM v$rollname a WHERE a.usn=9; USN NAME---------- ------------------------------ 9 _SYSSMU9$sys@ORCL> alter system dump undo header '_SYSSMU9$';System altered.sys@ORCL> select spid from v$process where addr in (select paddr from v$session where sid in (select sid 2 from v$mystat where rownum=1));SPID------------10086
部分內容摘入如下:
********************************************************************************Undo Segment: _SYSSMU9$ (9)******************************************************************************** Extent Control Header ----------------------------------------------------------------- Extent Header:: spare1: 0 spare2: 0 #extents: 4 #blocks: 31 last map 0x00000000 #maps: 0 offset: 4080 Highwater:: 0x00800615 ext#: 2 blk#: 4 ext size: 8 #blocks in seg. hdr's freelists: 0 #blocks below: 0 mapblk 0x00000000 offset: 2 Unlocked Map Header:: next 0x00000000 #extents: 4 obj#: 0 flag: 0x40000000 Extent Map ----------------------------------------------------------------- 0x0080008a length: 7 0x008000d9 length: 8 0x00800611 length: 8 0x00800669 length: 8 Retention Table ----------------------------------------------------------- Extent Number:0 Commit Time: 1344269736 Extent Number:1 Commit Time: 1344276385 Extent Number:2 Commit Time: 1344264118 Extent Number:3 Commit Time: 1344264252 TRN CTL:: seq: 0x0105 chd: 0x0019 ctl: 0x0014 inc: 0x00000000 nfb: 0x0000 mgc: 0x8201 xts: 0x0068 flg: 0x0001 opt: 2147483646 (0x7ffffffe) uba: 0x00800615.0105.11 scn: 0x0000.000d2615Version: 0x01 FREE BLOCK POOL:: uba: 0x00000000.0105.10 ext: 0x2 spc: 0x16f6 uba: 0x00000000.0105.03 ext: 0x2 spc: 0x1eac uba: 0x00000000.0101.0b ext: 0xb spc: 0x198c uba: 0x00000000.0000.00 ext: 0x0 spc: 0x0 uba: 0x00000000.0000.00 ext: 0x0 spc: 0x0 TRN TBL:: index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt ------------------------------------------------------------------------------------------------ 0x00 9 0x00 0x0162 0x0029 0x0000.000d2ab9 0x00800613 0x0000.000.00000000 0x00000001 0x00000000 1344280244 ............................................ 0x1a 10 0x80 0x0162 0x0002 0x0000.00000000 0x00800615 0x0000.000.00000000 0x00000001 0x00000000 0 ............................................
這就是神秘的復原段頭的資訊,其中包括事務表資訊。其中,0x1a是個16進位數,轉成10進位:
scott@ORCL> select to_number('1a','xx') from dual;TO_NUMBER('1A','XX')-------------------- 26
由此,可知:0x1a為26.回顧前面的事務資訊,該事務正好佔用的是第26號事務槽,狀態(state)為10代表的是活動事務。
dump舊值資訊
段頭資訊的第七列:dba(data block address),指的就是復原塊的地址0x00800615
下面把dba翻譯一下
sys@ORCL> select to_number('00800615','xxxxxxxx') from dual;TO_NUMBER('00800615','XXXXXXXX')-------------------------------- 8390165sys@ORCL> select dbms_utility.data_block_address_file(8390165) from dual;DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(8390165)--------------------------------------------- 2sys@ORCL> select dbms_utility.data_block_address_block(8390165) from dual;DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(8390165)---------------------------------------------- 1557
由此,可知:舊值在2號檔案的第1557個復原塊上
這與從v$transaction查詢得到的資料完全一致:
sys@ORCL> select ubafil,ubablk from v$transaction; UBAFIL UBABLK---------- ---------- 2 1557
回到會話1,繼續DML事務:scott使用者
scott@ORCL> update emp set sal=4000 where empno=7788;1 row updated.scott@ORCL> update emp set sal=4000 where empno=7782;1 row updated.scott@ORCL> update emp set sal=40000 where empno=7698;1 row updated.
會話2:sys使用者
把復原塊1557dump出來
sys@ORCL> select spid from v$process where addr in (select paddr from v$session where sid in (select sid from v$mystat where rownum=1));SPID------------11146sys@ORCL> alter system dump datafile 2 block 1557;System altered.
復原塊1557裡面開頭的資訊部分如下:
*** 2012-08-07 16:31:42.449*** SERVICE NAME:(SYS$USERS) 2012-08-07 16:31:42.355*** SESSION ID:(135.102) 2012-08-07 16:31:42.355Start dump data blocks tsn: 1 file#: 2 minblk 1557 maxblk 1557buffer tsn: 1 rdba: 0x00800615 (2/1557)scn: 0x0000.000d36d2 seq: 0x01 flg: 0x04 tail: 0x36d20201frmt: 0x02 chkval: 0xbeca type: 0x02=KTU UNDO BLOCKHex dump of block: st=0, typ_found=1Dump of memory from 0x0E7E4400 to 0x0E7E6400......................********************************************************************************UNDO BLK:xid: 0x0009.01a.00000162 seq: 0x105 cnt: 0x14 irb: 0x14 icl: 0x0 flg: 0x0000 Rec Offset Rec Offset Rec Offset Rec Offset Rec Offset---------------------------------------------------------------------------0x01 0x1f08 0x02 0x1e58 0x03 0x1df4 0x04 0x1da0 0x05 0x1d1c0x06 0x1ca0 0x07 0x1c54 0x08 0x1b80 0x09 0x1afc 0x0a 0x1a700x0b 0x198c 0x0c 0x1924 0x0d 0x18cc 0x0e 0x17e8 0x0f 0x17800x10 0x1728 0x11 0x16a8 0x12 0x1650 0x13 0x15f4 0x14 0x1598*-----------------------------
注意,這部分資訊有個參數irb: 0x14。irb指的是復原段中記錄的最近所有未提交的事務的開始之處,也就是最近一次被修改的地方,如果要復原,這便是復原的起點。
由上,可知:(irb: 0x14)的位移地址為0x1598
找到irb:0x14資訊,如下:
*-----------------------------* Rec #0x14 slt: 0x1a objn: 51148(0x0000c7cc) objd: 51148 tblspc: 4(0x00000004)* Layer: 11 (Row) opc: 1 rci 0x13Undo type: Regular undo Last buffer split: NoTemp Object: NoTablespace Undo: Nordba: 0x00000000*-----------------------------KDO undo record:KTB Redoop: 0x02 ver: 0x01op: C uba: 0x00800615.0105.13KDO Op code: URP row dependencies Disabled xtype: XA flags: 0x00000000 bdba: 0x01000020 hdba: 0x0100001bitli: 2 ispac: 0 maxfr: 4858tabn: 0 slot: 5(0x5) flag: 0x2c lock: 0 ckix: 191ncol: 8 nnew: 1 size: 1col 5: [ 3] c2 1d 33
c2 1d 33轉換為10進位就是2850.這是最後被更新的記錄的舊值。也就是update emp set sal=4000 where empno=7698。
注意,這裡有幾個參數需要關註:
1)參數rci 0x13,該參數代表的就是undo chain(同一個事務中的多次修改,根據chain連結關聯),此處的rci 0x13指向第二次修改update emp set sal=4000 where empno=7782.此時,如果有其他進程查詢scott.emp表,則oracle需要構造一致性讀來把資料展現給使用者。
2)參數bdba: 0x01000020,該參數代表舊值對應的資料區塊的地址,下面把這個參數翻譯出來:
scott@ORCL> select to_number('01000020','xxxxxxxx') from dual;TO_NUMBER('01000020','XXXXXXXX')-------------------------------- 16777248scott@ORCL> select dbms_utility.data_block_address_file(16777248) from dual;DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(16777248)---------------------------------------------- 4scott@ORCL> select dbms_utility.data_block_address_block(16777248) from dual;DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(16777248)----------------------------------------------- 32
再將4號檔案的第32個塊dump出來:
sys@ORCL> select spid from v$process where addr in (select paddr from v$session 2 where sid in (select sid from v$mystat where rownum=1));SPID------------13929sys@ORCL> alter system dump datafile 4 block 32;System altered.
部分內容摘入如下:
Start dump data blocks tsn: 4 file#: 4 minblk 32 maxblk 32buffer tsn: 4 rdba: 0x01000020 (4/32)scn: 0x0000.000d36d2 seq: 0x01 flg: 0x04 tail: 0x36d20601frmt: 0x02 chkval: 0xa9ea type: 0x06=trans dataBlock header dump: 0x01000020 Object id on Block? Y seg/obj: 0xc7cc csc: 0x00.b9cf3 itc: 2 flg: E typ: 1 - DATA brn: 0 bdba: 0x1000019 ver: 0x01 opc: 0 inc: 0 exflg: 0 Itl Xid Uba Flag Lck Scn/Fsc0x01 0x0003.011.000000f2 0x00805794.00c8.49 C--- 0 scn 0x0000.0006bfdb0x02 0x0009.01a.00000162 0x00800615.0105.14 ---- 3 fsc 0x0002.00000000
這裡存在ITL事務槽資訊,事務必須得到一個ITL事務槽才能夠進行資料修改。ITL內容主要包括xid(指向復原段頭的ITL)、uba(指向復原塊)、Lck(行頭鎖的個數,此處修改三次,故有3個鎖)。在以上輸出中,可知:itl2(0x02)上存在活動事務。
將xid(0x0009.01a.00000162)分解一下:0009表明該事務指向9號復原段,01a表明該事務指向26號事務槽,00000162表明該事務第354次被覆蓋(因為復原段是迴圈使用的)。
將uba(0x00800615.0105.14)分解一下:00800615表明第1557個復原塊,14是irb資訊(復原的起點)。
至此,undo段和資料區塊裡面的事務資訊被完全解析出來,二者完全一致!