探索undo一致性读

1. 按照以下步骤模拟一致性读

SQL> create table test(id int,name char(2000));

Table created.

SQL> insert into test values(1,'AAAAA');     

1 row created.

SQL> commit;

Commit complete.

SQL> select current_scn from v$database;

CURRENT_SCN
-----------
 2097349
SQL> var x refcursor
SQL> exec open :x for select * from test where id=1;

PL/SQL procedure successfully completed.

SQL> update test set name='BBBBB' where id=1;

1 row updated.

SQL> commit;

Commit complete.

SQL> update test set name='CCCCC' where id=1;

1 row updated.

SQL> commit;

Commit complete.

SQL> update test set name='DDDDD' where id=1;

1 row updated.

SQL> commit;

Commit complete.

SQL> update test set name='EEEEE' where id=1;

1 row updated.

SQL> print :x

        ID
----------
NAME
--------------------------------------------------------------------------------
         1
AAAAA                /*可以看到此时结果依然是'AAAAA',即打开游标,select发生的时间TEST的值*/

2. 查看当前数据块的dump内容:

SQL> select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid) from test where id=1;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------------ ------------------------------------
                                   1                                94673

SQL> alter system dump datafile 1 block 94673;

System altered.

SQL> select * from v$diag_info;

trace文件分析:

Block header dump:  0x004171d1
 Object id on Block? Y
 seg/obj: 0x15b54  csc: 0x00.200101  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000a.01b.00000554  0x00c007c1.00ba.1b  ----    1  fsc 0x0000.00000000
0x02   0x0006.000.000006e3  0x00c0014b.013d.01  C---    0  scn 0x0000.002000fb
bdba: 0x004171d1
data_block_dump,data header at 0x7f8c75e0fa5c
===============
tsiz: 0x1fa0
hsiz: 0x14
pbl: 0x7f8c75e0fa5c
     76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x17c7
avsp=0x17b3
tosp=0x17b3
0xe:pti[0]      nrow=1  offs=0
0x12:pri[0]     offs=0x17c7
block_row_dump:
tab 0, row 0, @0x17c7
tl: 2009 fb: --H-FL-- lb: 0x1  cc: 2
col  0: [ 2]  c1 02
col  1: [2000]
 45 45 45 45 45 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 /*

可以看到此时数据块内的值为‘45,45,45,45,45’,将45从16进制转换为10进制,在转换为字符

SQL> select to_number('45','xx') from dual;

TO_NUMBER('45','XX')
--------------------
                  69
SQL> select CHR(69) from dual;

C
-
E
即数据块内的数据为'EEEEE',但是'EEEEE'这行上面记录的是有行锁的,

tl: 2009 fb: --H-FL-- lb: 0x1  cc: 2

***********************************************************************************************************

 Itl           Xid                                             Uba         Flag  Lck        Scn/Fsc
0x01   0x000a.01b.00000554  0x00c007c1.00ba.1b  ----    1  fsc 0x0000.00000000
0x02   0x0006.000.000006e3  0x00c0014b.013d.01  C---    0  scn 0x0000.002000fb

***********************************************************************************************************

其对应的事务槽为0x01,通过上面的ITL槽1也可以看出,是没有提交标记的,因为oracle事务的隔离级别为RC(read commited),即读取提交后的数据,所以'EEEEE'并非oracle读取的数据,而是需要读取'EEEEE'提交前的值,可以根据ITL槽1上面记录的UBA找到'EEEEE'被修改前的值所存放的undo block地址。

其实这里不光是RC读,还涉及到一致性读:

oracle服务器进程在扫描表T的数据时,会把扫描到的数据块头部的ITL槽中的SCN号与select语句发生的时间点SCN进行比较。如果数据块头部的SCN号比select SCN要小,说明该数据块在select之后没有本更新,可以直接读取其中的数据;否则,如果数据块头部ITL槽的SCN号比select SCN要大,说明该数据块在select之后被更新了,该块里的数据已经不是select那个时间点的数据了,于是需要借助undo数据块取出被修改前的数据,再结合数据块里的数据行,从而构造出select那个时间点的数据块内容,这样的数据块也叫作CR(Consistent Read)块。对于delete来说,其undo信息就是insert,也就是说构建出来的CR块中就插入了被删除的那条记录。

3. 根据UBA:0x00c007c1.00ba.1b来查找'EEEEE' 被修改前的值‘DDDDD’

UBA解析:

    00c007c1:dba(data block address)

    00ba:seq# (sequence number)

    1b:rec# (record number)

SQL> SELECT DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(TO_NUMBER('00c007c1',  
  2                                                        'xxxxxxxxxxxx')) FILE_ID,  
  3         DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00c007c1',  
  4                                                         'xxxxxxxxxxxx')) BLOCK_ID  
  5    FROM DUAL;

   FILE_ID   BLOCK_ID
---------- ----------
         3       1985    

SQL> select to_number('00ba','xxxx') from dual;

TO_NUMBER('00BA','XXXX')
------------------------
                     186

SQL> select to_number('1b','xx') from dual;

TO_NUMBER('1B','XX')
--------------------
                  27

可以根据V$TRANSACTION验证下:

SQL> select ADDR,UBAFIL,UBABLK,UBASQN,UBAREC from v$transaction;

ADDR                 UBAFIL     UBABLK     UBASQN     UBAREC
---------------- ---------- ---------- ---------- ----------
000000008BD53150          3       1985        186         27

接着dump uba对应的数据块:3号文件 1985号块 1b(27) 号record

SQL> alter system dump datafile 3 block 1985;

System altered.

注:因为Rec是以16进制记录的,所以查找的时候要用1b,用27是查不到的

trace文件内容:

*-----------------------------
* Rec #0x1b  slt: 0x1b  objn: 88916(0x00015b54)  objd: 88916  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: 0x00c007c1.00ba.18 ctl max scn: 0x0000.001fac87 prv tx scn: 0x0000.001facc9
txn start scn: scn: 0x0000.002001a7 logon user: 0
 prev brb: 12584892 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0x0007.01e.0000055d uba: 0x00c000e1.0111.02
                      flg: C---    lkc:  0     scn: 0x0000.002000f4
Array Update of 1 rows:
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12
ncol: 2 nnew: 1 size: 0
KDO Op code:  21 row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x004171d1  hdba: 0x004171d0
itli: 1  ispac: 0  maxfr: 4863
vect = 3
col  1: [2000]
 44 44 44 44 44 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
可以看到,该UNDO数据块里记录的值为'44,44,44,44,44',即'D,D,D,D,D',也就是'E,E,E,E,E'修改前的值。

*****************************************************************************************************

op: L  itl: xid:  0x0007.01e.0000055d uba: 0x00c000e1.0111.02
                      flg: C---    lkc:  0     scn: 0x0000.002000f4

******************************************************************************************************

该undo数据块的ITL信息记录的scn为:

SQL> select to_number('002001a7','xxxxxxxx') from dual;

TO_NUMBER('002001A7','XXXXXXXX')
--------------------------------
   2097575
还是比select scn要大,说明仍然需要找'DDDDD'这个值被修改前的影响,那我们到哪个uba去找?是到该undo块里记录的uba去找?NO

WHAT THE FUCK?

因为数据块上的事务槽是交替使用的,并且当事务提交或者回滚之后,ITL槽就可以被重复使用了(覆盖),所以ITL槽1将'DDDDD'修改为'EEEEE',其对应的UBA记录的是'DDDDD'的内容,而ITL槽2则是前一个DML操作,即将'CCCCC'修改为'DDDDD',所以ITL槽2上对应的UBA才是'DDDDD'被修改前的值‘CCCCC’,那么存放'DDDDD'这个值的undo块里面存放的uba对应的是哪个值呢?

秘密就在于,oracle在记录Undo数据的时候,不仅记录了改变前的数据(DDDDD),还记录了改变前的数据所在的数据块头部的ITL信息。

推理如下:当前数据块上的两个事物槽,对应的uba分别存放的是,‘CCCCC’,‘DDDDD’,如果再有一个DML将'EEEEE'更改为'FFFFF',那么将重用'CCCCC'这个值对应的ITL槽,即ITL槽2,那么update为'FFFFF'的新ITL槽将会记录'EEEEE'的undo信息,其中记录的即为被覆盖掉的ITL槽2的信息(对应的UBA为'CCCCC')。

所以,存放'DDDDD'这个前映像的undo块上记录的是'BBBBB'的信息。

其实,简单的说,我们可以通过比较SCN的方法来确定前一个undo块应该是哪一个

'DDDDD‘所在undo块上记录的ITL信息中SCN为:

SQL> select to_number('002000f4','xxxxxxxx') from dual;

TO_NUMBER('002000F4','XXXXXXXX')
--------------------------------
                         2097396

ITL槽2上对应的scn为:

SQL> select to_number('002000fb','xxxxxxxx') from dual;

TO_NUMBER('002000FB','XXXXXXXX')
--------------------------------
                         2097403
是比‘DDDDD’所在undo块的scn要大的,所以接下来dump ITL槽2上uba对应的undo block信息

4. 根据ITL槽2上记录uba查找'DDDDD'的前映像 0x00c0014b.013d.01

SQL>     SELECT DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(TO_NUMBER('00c0014b',  
  2                                                            'xxxxxxxxxxxx')) FILE_ID,  
  3             DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00c0014b',  
  4                                                             'xxxxxxxxxxxx')) BLOCK_ID  
  5        FROM DUAL;

   FILE_ID   BLOCK_ID
---------- ----------
         3        331

trace文件内容:

*-----------------------------
* Rec #0x1  slt: 0x00  objn: 88916(0x00015b54)  objd: 88916  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: 0x00c0014a.013d.04 ctl max scn: 0x0000.001fad16 prv tx scn: 0x0000.001fad50
txn start scn: scn: 0x0000.002000f4 logon user: 0
 prev brb: 12583125 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0x0005.010.0000061e uba: 0x00c000c3.00ec.15
                      flg: C---    lkc:  0     scn: 0x0000.002000ed
Array Update of 1 rows:
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12
ncol: 2 nnew: 1 size: 0
KDO Op code:  21 row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x004171d1  hdba: 0x004171d0
itli: 2  ispac: 0  maxfr: 4863
vect = 3
col  1: [2000]
 43 43 43 43 43 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 /*即CCCCC*/

***********************************************************************************************************

op: L  itl: xid:  0x0005.010.0000061e uba: 0x00c000c3.00ec.15
                      flg: C---    lkc:  0     scn: 0x0000.002000ed

***********************************************************************************************************

比较'CCCCC'所在undo块的SCN和select SCN

SQL> select to_number('002000f4','xxxxxxxx') from dual;

TO_NUMBER('002000F4','XXXXXXXX')
--------------------------------
        2097396

还是要大,说明还需要找'CCCCC‘被修改前的值

这里,比较下'CCCCC'所在undo块上ITL的scn与'DDDDD'所在undo块的SCN大小

SQL> select to_number('002000ed','xxxxxxxx') from dual;

TO_NUMBER('002000ED','XXXXXXXX')
--------------------------------
                         2097389

'CCCCC'(2097389)<'DDDDD'(2097396)

所以接下来根据'DDDDD'所在undo块上记录的ITL槽信息(uba)寻找'CCCCC'被修改前的值

5. 根据'DDDDD'所在undo块上uba查找'CCCCC'的前映像(BBBBB)   0x00c000e1.0111.02

SQL>     SELECT DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(TO_NUMBER('00c000e1',  
  2                                                            'xxxxxxxxxxxx')) FILE_ID,  
  3             DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00c000e1',  
  4                                                             'xxxxxxxxxxxx')) BLOCK_ID  
  5        FROM DUAL;  

   FILE_ID   BLOCK_ID
---------- ----------
         3        225
SQL> alter system dump datafile 3 block 225;

System altered.

trace文件内容:

*-----------------------------
* Rec #0x2  slt: 0x1e  objn: 88916(0x00015b54)  objd: 88916  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: 0x00c000e1.0111.01 ctl max scn: 0x0000.001facfb prv tx scn: 0x0000.001fad37
txn start scn: scn: 0x0000.002000ed logon user: 0
 prev brb: 12583267 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: L  itl: xid:  0x0003.008.00000632 uba: 0x00c00947.015f.33
                      flg: C---    lkc:  0     scn: 0x0000.002000b6
Array Update of 1 rows:
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12
ncol: 2 nnew: 1 size: 0
KDO Op code:  21 row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x004171d1  hdba: 0x004171d0
itli: 1  ispac: 0  maxfr: 4863
vect = 3
col  1: [2000]
 42 42 42 42 42 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20

*****************************************************************************************************************

op: L  itl: xid:  0x0003.008.00000632 uba: 0x00c00947.015f.33
                      flg: C---    lkc:  0     scn: 0x0000.002000b6

******************************************************************************************************************

比较此时的SCN与select SCN

SQL> select to_number('002000ed','xxxxxxxx') from dual;

TO_NUMBER('002000ED','XXXXXXXX')
--------------------------------
                         2097389
还是比select SCN要大

所以接下来dump 'CCCCC' Undo块上对应的UBA信息

6. 根据'CCCCC'所在undo块上的uba信息,查找'BBBBB'被修改前的值(AAAAA) 0x00c000c3.00ec.15

SQL>     SELECT DBMS_UTILITY.DATA_BLOCK_ADDRESS_FILE(TO_NUMBER('00c000c3',  
  2                                                            'xxxxxxxxxxxx')) FILE_ID,  
  3             DBMS_UTILITY.DATA_BLOCK_ADDRESS_BLOCK(TO_NUMBER('00c000c3',  
  4                                                             'xxxxxxxxxxxx')) BLOCK_ID  
  5        FROM DUAL;  

   FILE_ID   BLOCK_ID
---------- ----------
         3        195

SQL> alter system dump datafile 3 block 195;

System altered.

trace文件内容:

*-----------------------------
* Rec #0x15  slt: 0x10  objn: 88916(0x00015b54)  objd: 88916  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: 0x00c000c3.00ec.12 ctl max scn: 0x0000.001facf9 prv tx scn: 0x0000.001fad35
txn start scn: scn: 0x0000.002000b6 logon user: 0
 prev brb: 12585470 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x03  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: Z
Array Update of 1 rows:
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 12
ncol: 2 nnew: 1 size: 0
KDO Op code:  21 row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x004171d1  hdba: 0x004171d0
itli: 2  ispac: 0  maxfr: 4863
vect = 3
col  1: [2000]
 41 41 41 41 41 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20

终于找到了'AAAAA'....

在比较‘AAAAA’所在undo块的scn与select SCN

SQL> select to_number('002000b6','xxxxxxxx') from dual;

TO_NUMBER('002000B6','XXXXXXXX')
--------------------------------
                         2097334

可以看到,是比select SCN要小的,所以'AAAAA'才是oracle最终读数的内容









你可能感兴趣的:(探索undo一致性读)