Oracle undo 镜像数据探究
 今天是2013-08-18,隔别一周的网络终于可以上网了。幸福啊。
 我们都知道,undo是为了保证一致性读的,也就是说你在对更改的数据但是没有提交,那么其他session是无法查看到你更改的内容的,如果对方
 进行读取数据,依然是之前的数据,undo就是保存了数据的前镜像。看完我写的这篇文章之后,你就知道undo是怎么保存前镜像的以及它的内部机
 制是什么。现在开始挖掘undo是怎么保存前镜像的。
 1)首先更新一条数据,但是不进行commit如下:
SQL> col name for a50     
SQL> select obj#,name from t where rownum<5;

      OBJ# NAME
---------- --------------------------------------------------
        46 I_USER1
        28 Rhys1
        15 RHYS20
        29 RHYS3

SQL> update t set name='rhys1' where obj#=46;

1 row updated.

SQL> update t set name='rhys2' where obj#=28;

1 row updated.

SQL> update t set name='rhys3' where obj#=15;

1 row updated.

SQL> update t set name='rhys4' where obj#=29;

1 row updated.

SQL> select xidusn,xidslot,ubafil,ubablk from v$transaction;

    XIDUSN    XIDSLOT     UBAFIL     UBABLK
---------- ---------- ---------- ----------
        12          7          8       1493

SQL> select * from v$rollname where usn=12;

       USN NAME
---------- ------------------------------
        12 _SYSSMU12_584745277$

SQL> select usn,status,curext from v$rollstat;

       USN STATUS              CUREXT
---------- --------------- ----------
         0 ONLINE                   5
        11 ONLINE                   4
        12 ONLINE                   3
        13 ONLINE                   2
        14 ONLINE                   3
        15 ONLINE                   2
        16 ONLINE                   2
        17 ONLINE                  14
        18 ONLINE                   4
        19 ONLINE                   3
        20 ONLINE                   2

11 rows selected.

SQL> select usn,status,curext,xacts from v$rollstat where xacts>0;

       USN STATUS              CUREXT      XACTS
---------- --------------- ---------- ----------
        12 ONLINE                   3          1

SQL> alter system dump undo header '_SYSSMU12_584745277$';

System altered.

SQL> alter system dump datafile 8 block 1493;

System altered.

SQL> col name for a80
SQL> col inst_id for 9999 
SQL> col value for a80
SQL> select * from v$diag_info;

INST_ID NAME                                                                             VALUE
------- -------------------------------------------------------------------------------- --------------------------------------------------------------------------------
      1 Diag Enabled                                                                     TRUE
      1 ADR Base                                                                         /opt/app/oracle
      1 ADR Home                                                                         /opt/app/oracle/diag/rdbms/rhys/RHYS
      1 Diag Trace                                                                       /opt/app/oracle/diag/rdbms/rhys/RHYS/trace
      1 Diag Alert                                                                       /opt/app/oracle/diag/rdbms/rhys/RHYS/alert
      1 Diag Incident                                                                    /opt/app/oracle/diag/rdbms/rhys/RHYS/incident
      1 Diag Cdump                                                                       /opt/app/oracle/diag/rdbms/rhys/RHYS/cdump
      1 Health Monitor                                                                   /opt/app/oracle/diag/rdbms/rhys/RHYS/hm
      1 Default Trace File                                                               /opt/app/oracle/diag/rdbms/rhys/RHYS/trace/RHYS_ora_5284.trc
      1 Active Problem Count                                                             1
      1 Active Incident Count                                                            1

11 rows selected.
2)从以上步骤中,我们可以获得的信息如下:
    该事务使用的undo段为12,其区为3
    undo段的名字为“_SYSSMU12_584745277$”
   该事务在undo的第7个槽位,数据文件号为8,使用的数据块为1493;
3)我们开始挖掘undo内部机制,首先转储undo header;(摘录信息如下)
********************************************************************************
Undo Segment:  _SYSSMU12_584745277$ (12)
********************************************************************************
  Extent Control Header
  -----------------------------------------------------------------
  Extent Header:: spare1: 0      spare2: 0      #extents: 4      #blocks: 271  
                  last map  0x00000000  #maps: 0      offset: 4080 
      Highwater::  0x020005d5  ext#: 3      blk#: 85     ext size: 128  
  #blocks in seg. hdr's freelists: 0    
  #blocks below: 0    
  mapblk  0x00000000  offset: 3    
                   Unlocked
     Map Header:: next  0x00000000  #extents: 4    obj#: 0      flag: 0x40000000
  Extent Map
  -----------------------------------------------------------------
   0x02000091  length: 7    
   0x02000168  length: 8    
   0x02000280  length: 128  
   0x02000580  length: 128  
 
 Retention Table
  -----------------------------------------------------------
 Extent Number:0  Commit Time: 1376748046
 Extent Number:1  Commit Time: 1376748092
 Extent Number:2  Commit Time: 1376804143
 Extent Number:3  Commit Time: 1376804143
 
  TRN CTL:: seq: 0x033a chd: 0x0020 ctl: 0x0001 inc: 0x00000000 nfb: 0x0000
            mgc: 0xb000 xts: 0x0068 flg: 0x0001 opt: 2147483646 (0x7ffffffe)
            uba: 0x020005d5.033a.24 scn: 0x0000.009b9276
Version: 0x01
  FREE BLOCK POOL::
    uba: 0x00000000.033a.23 ext: 0x3  spc: 0xc78  
    uba: 0x00000000.033a.27 ext: 0x3  spc: 0xb20  
    uba: 0x00000000.033a.08 ext: 0x3  spc: 0x1c66 
    uba: 0x00000000.033a.45 ext: 0x3  spc: 0x200  
    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  0x0059  0x0016  0x0000.009b9292  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x01    9    0x00  0x005a  0xffff  0x0000.009b94e5  0x020005d5  0x0000.000.00000000  0x00000001   0x00000000  1376806887
   0x02    9    0x00  0x005a  0x000a  0x0000.009b9461  0x020005d5  0x0000.000.00000000  0x00000001   0x00000000  1376806707
   0x03    9    0x00  0x0059  0x0017  0x0000.009b930e  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x04    9    0x00  0x0059  0x0005  0x0000.009b92cf  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x05    9    0x00  0x0059  0x0008  0x0000.009b92d8  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x06    9    0x00  0x0059  0x0009  0x0000.009b92aa  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x07   10    0x80  0x005a  0x0003  0x0000.00000000  0x020005d5  0x0000.000.00000000  0x00000001   0x00000000  0
   0x08    9    0x00  0x0059  0x001b  0x0000.009b92e1  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x09    9    0x00  0x0059  0x000c  0x0000.009b92b6  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x0a    9    0x00  0x0059  0x001f  0x0000.009b9479  0x020005d5  0x0000.000.00000000  0x00000001   0x00000000  1376806707
   0x0b    9    0x00  0x0059  0x001a  0x0000.009b92fc  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203
   0x0c    9    0x00  0x0059  0x0004  0x0000.009b92c3  0x020005d3  0x0000.000.00000000  0x00000001   0x00000000  1376806203

可以看到在事务表中0x07正是第七个slot,把dba转为2进制,在转为10进制,可以打出数据文件号为8,数据块为1493,这正是数据的前镜像。
4)我们已经在 undo header中找到了数据的前镜像,那么我们在看一下这个前镜像到底是什么?
摘录数据文件信息如下:
********************************************************************************
UNDO BLK: 
xid: 0x000c.007.0000005a  seq: 0x33a cnt: 0x27  irb: 0x27  icl: 0x0   flg: 0x0000
 
 Rec Offset      Rec Offset      Rec Offset      Rec Offset      Rec Offset
---------------------------------------------------------------------------
0x01 0x1f4c     0x02 0x1ec4     0x03 0x1db0     0x04 0x1d3c     0x05 0x1cb8    
0x06 0x1c4c     0x07 0x1be8     0x08 0x1b94     0x09 0x1aec     0x0a 0x1a4c    
0x0b 0x19ac     0x0c 0x1900     0x0d 0x1854     0x0e 0x17ac     0x0f 0x171c    
0x10 0x167c     0x11 0x15d4     0x12 0x1544     0x13 0x14a4     0x14 0x13f8    
0x15 0x139c     0x16 0x1328     0x17 0x128c     0x18 0x117c     0x19 0x1114    
0x1a 0x10bc     0x1b 0x1068     0x1c 0x100c     0x1d 0x0f98     0x1e 0x0f3c    
0x1f 0x0ee8     0x20 0x0e60     0x21 0x0dd4     0x22 0x0d6c     0x23 0x0cd0    
0x24 0x0c2c     0x25 0x0bb0     0x26 0x0b50     0x27 0x0ad4
可以看到数据记录为39,这是第一条,并且最后一个偏移量也为39,这正是rollback的开始。

*-----------------------------
* Rec #0x27  slt: 0x07  objn: 73229(0x00011e0d)  objd: 73229  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x26  
Undo type:  Regular undo   Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo
op: 0x02  ver: 0x01 
compat bit: 4 (post-11) padding: 1
op: C  uba: 0x020005d5.033a.26
Array Update of 1 rows:
tabn: 0 slot: 109(0x6d) flag: 0x2c lock: 0 ckix: 0
ncol: 18 nnew: 1 size: 0
KDO Op code:  21 row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x0041e7f4  hdba: 0x00414908
itli: 1  ispac: 0  maxfr: 4863
vect = 8
col  3: [ 5]  52 48 59 53 33

好了,既然找到了,那么我们看一下这条record记录了是数据的是什么.

前镜像为:RHYS3

SQL>
这是最后一条SQL> update t set name='rhys4' where obj#=29;的前镜像。因为 开始部分是从倒叙的,因此,我们在看倒数第二条。
*-----------------------------
* Rec #0x26  slt: 0x07  objn: 73229(0x00011e0d)  objd: 73229  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x25  
Undo type:  Regular undo   Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo
op: 0x02  ver: 0x01 
compat bit: 4 (post-11) padding: 1
op: C  uba: 0x020005d5.033a.25
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0041e7f4  hdba: 0x00414908
itli: 1  ispac: 0  maxfr: 4863
tabn: 0 slot: 108(0x6c) flag: 0x2c lock: 0 ckix: 0
ncol: 18 nnew: 1 size: 1
col  3: [ 6]  52 48 59 53 32 30
前镜像为:
SQL> select utl_raw.cast_to_varchar2(replace('52 48 59 53 32 30',' ') ) nam from dual;

NAM
--------------------------------------------------------------------------------
RHYS20

SQL>
第三条语句:
*-----------------------------
* Rec #0x25  slt: 0x07  objn: 73229(0x00011e0d)  objd: 73229  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x24  
Undo type:  Regular undo   Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo
op: 0x02  ver: 0x01 
compat bit: 4 (post-11) padding: 1
op: C  uba: 0x020005d5.033a.24
Array Update of 1 rows:
tabn: 0 slot: 107(0x6b) flag: 0x2c lock: 0 ckix: 0
ncol: 18 nnew: 1 size: 0
KDO Op code:  21 row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x0041e7f4  hdba: 0x00414908
itli: 1  ispac: 0  maxfr: 4863
vect = 8
col  3: [ 5]  52 68 79 73 31
前镜像为:Rhys1
在看第四条:
*-----------------------------
* Rec #0x24  slt: 0x07  objn: 73229(0x00011e0d)  objd: 73229  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x00  
Undo type:  Regular undo    Begin trans    Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x020005d5.033a.23 ctl max scn: 0x0000.009b926d prv tx scn: 0x0000.009b9276
txn start scn: scn: 0x0000.009b950d logon user: 0
 prev brb: 33555923 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04  ver: 0x01 
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0x0011.00d.0000003e uba: 0x02000230.01ee.3a
                      flg: C---    lkc:  0     scn: 0x0000.009b4ab6
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0041e7f4  hdba: 0x00414908
itli: 1  ispac: 0  maxfr: 4863
tabn: 0 slot: 106(0x6a) flag: 0x2c lock: 0 ckix: 0
ncol: 18 nnew: 1 size: 2
col  3: [ 7]  49 5f 55 53 45 52 31
前镜像为:I_USER1

至此,在回过头来,看看 刚刚开始update的数据。
SQL> select obj#,name from t where rownum<5;

      OBJ# NAME
---------- --------------------------------------------------
        46 I_USER1
        28 Rhys1
        15 RHYS20
        29 RHYS3

SQL> update t set name='rhys1' where obj#=46;

1 row updated.

SQL> update t set name='rhys2' where obj#=28;

1 row updated.

SQL> update t set name='rhys3' where obj#=15;

1 row updated.

SQL> update t set name='rhys4' where obj#=29;

1 row updated.

oracle,就是这样存储数据前镜像的。哈哈。

继续研究,然后拿出undo record的一条记录如下:
*-----------------------------
* Rec #0x24  slt: 0x07  objn: 73229(0x00011e0d)  objd: 73229  tblspc: 0(0x00000000)
*       Layer:  11 (Row)   opc: 1   rci 0x00  
Undo type:  Regular undo    Begin trans    Last buffer split:  No
Temp Object:  No
Tablespace Undo:  No
rdba: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x020005d5.033a.23 ctl max scn: 0x0000.009b926d prv tx scn: 0x0000.009b9276
txn start scn: scn: 0x0000.009b950d logon user: 0
 prev brb: 33555923 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04  ver: 0x01 
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0x0011.00d.0000003e uba: 0x02000230.01ee.3a
                      flg: C---    lkc:  0     scn: 0x0000.009b4ab6
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0041e7f4  hdba: 0x00414908
itli: 1  ispac: 0  maxfr: 4863
tabn: 0 slot: 106(0x6a) flag: 0x2c lock: 0 ckix: 0
ncol: 18 nnew: 1 size: 2
col  3: [ 7]  49 5f 55 53 45 52 31
可以看到在record中也记录了该undo对应的前镜像的数据块的地址信息,bdba。
我们进行转换得到:0000000001 0000011110011111110100 可以得到是file 1,block 124916.
可以断定该表存在于system,如下:
SQL> col file_name for a70                      
SQL>  select a.table_name,a.tablespace_name,b.file_name from user_tables a,dba_data_files b where a.tablespace_name=b.tablespace_name and a.table_name='T';

TABLE_NAME                     TABLESPACE_NAME      FILE_NAME
------------------------------ -------------------- ----------------------------------------------------------------------
T                              SYSTEM               /opt/app/oracle/RHYS/system01.dbf

SQL>
然后,在跟随盖老师,看一下system01.dbf这个数据文件内容信息。转储信息如下:
Block header dump:  0x0041e7f4
 Object id on Block? Y
 seg/obj: 0x11e0d  csc: 0x00.9bb647  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x41e7f3 ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000c.007.0000005a  0x020005d5.033a.27  ----    4  fsc 0x0003.00000000
0x02   0x0014.001.00000044  0x02000628.037b.06  C---    0  scn 0x0000.009b94a8
bdba: 0x0041e7f4
data_block_dump,data header at 0x7f6e9e263a5c

当我们修改数据的时候会对相应的数据加锁,更直接说该 锁存在于数据块中,并且存在itl(事务槽信息),可以现在数据块中itl的详细内容了
其中包括xid,uba,flag,lock status,scn
xid代表事务id,其中包括undo段号,槽位号,wrap,然后我们转换一下该xid,可以看到undo.segment.number 为12,占用的槽位是7,wrap#为:90
我们查看一下事务视图进行一下确认。
  1* select xidusn,xidslot,xid,name,ubasqn,ubarec,ubafil,ubablk from v$transaction where xidusn=12

    XIDUSN    XIDSLOT XID              NAME           UBASQN     UBAREC     UBAFIL     UBABLK
---------- ---------- ---------------- ---------- ---------- ---------- ---------- ----------
        12          7 0C0007005A000000                   826         39          8       1493

SQL>
然后摘录一下undo header事务表部分如下:
0x07   10    0x80  0x005a  0x0003  0x0000.00000000  0x020005d5  0x0000.000.00000000  0x00000001   0x00000000  0
然后我们在看一下 这个事务的信息;
eg:
SQL> select xid,start_scn,commit_scn,operation,table_name,row_id,undo_sql from flashback_transaction_query where xid='0C0007005A000000';
 
XID               START_SCN COMMIT_SCN OPERATION                        TABLE_NAME                                                                       ROW_ID              UNDO_SQL
---------------- ---------- ---------- -------------------------------- -------------------------------------------------------------------------------- ------------------- --------------------------------------------------------------------------------
0C0007005A000000   10196237            UNKNOWN                          T                                                                                                   
0C0007005A000000   10196237            UNKNOWN                          T                                                                                                   
0C0007005A000000   10196237            UNKNOWN                          T                                                                                                   
0C0007005A000000   10196237            UNKNOWN                          T                                                                                                   
0C0007005A000000   10196237            BEGIN                                                                                                                                
 
SQL>
为什么undo_sql没有信息呢?我想是没有commit。稍后再看。
在数据块中的itl中存在一个uba,这正是该数据的前镜像信息。0x020005d5.033a.27转换一下,为20005d5(前镜像地址),033a为事务sequence号,
27为irb
如:
********************************************************************************
UNDO BLK: 
xid: 0x000c.007.0000005a  seq: 0x33a cnt: 0x27  irb: 0x27  icl: 0x0   flg: 0x0000

貌似有点乱,整理一下思路。
当执行个update语句的时候,首先在segment上分配段分配slot,然后再数据块头部记录itl信息指向undo record,undo record记录了数据的前镜像,
并且从最后一个记录开始到第一个执行update语句,这是一个倒叙研究查看过程,随后在数据块itl中存储锁信息,指定该数据块状态。然后再修改数据。
对了,现在看一下数据块锁的信息:
eg:
SQL> SELECT *  FROM V$MYSTAT WHERE ROWNUM<2;

       SID STATISTIC#      VALUE
---------- ---------- ----------
         1          0          0

SQL>
SQL> select a.sid,a.lmode,a.id1,b.type,b.name,b.description from v$lock a,v$lock_type b where a.sid=1 and a.type=b.type;
 
       SID      LMODE        ID1 TYPE                                                             NAME                                                             DESCRIPTION
---------- ---------- ---------- ---------------------------------------------------------------- ---------------------------------------------------------------- --------------------------------------------------------------------------------
         1          4        100 AE                                                               Edition Lock                                                     Prevent Dropping an edition in use
         1          3      73229 TM                                                               DML                                                              Synchronizes accesses to an object
         1          6     786439 TX                                                               Transaction                                                      Lock held by a transaction to allow other transactions to wait for it
 
SQL>
在数据块中的itl中锁的状态为3.代表Prevent Dropping an edition in use。
好了。就到 这把,另外在undo中也存有rdba信息,在学习redo的时候会进行联系,深入。