作者:dbtan |【转载时请以超链接形式标明文章出处作者信息】


回滚机制的深入研究:

如果大家有兴趣深入了解一下回滚段的机制,那么请跟随我将前面的例子进一步深化。

1. 从DML更新事务开始:
重新来看这个更新语句:

sys@TQGZS11G> conn scott/tiger
Connected.
scott@TQGZS11G> select * from emp;
     EMPNO ENAME           JOB                    MGR HIREDATE               SAL       COMM     DEPTNO
---------- --------------- --------------- ---------- --------------- ---------- ---------- ----------
      7369 SMITH           CLERK                 7902 17-DEC-80              800                    20
      7499 ALLEN           SALESMAN              7698 20-FEB-81             1600        300         30
      7521 WARD            SALESMAN              7698 22-FEB-81             1250        500         30
      7566 JONES           MANAGER               7839 02-APR-81             2975                    20
      7654 MARTIN          SALESMAN              7698 28-SEP-81             1250       1400         30
      7698 BLAKE           MANAGER               7839 01-MAY-81             2850                    30
     7782 CLARK           MANAGER               7839 09-JUN-81             2450                    10
      7788 SCOTT           ANALYST               7566 19-APR-87             3000                    20
      7839 KING            PRESIDENT                  17-NOV-81             5000                    10
      7844 TURNER          SALESMAN              7698 08-SEP-81             1500          0         30
      7876 ADAMS           CLERK                 7788 23-MAY-87             1100                    20
      7900 JAMES           CLERK                 7698 03-DEC-81              950                    30
      7902 FORD            ANALYST               7566 03-DEC-81             3000                    20
      7934 MILLER          CLERK                 7782 23-JAN-82             1300                    10
14 rows selected.
scott@TQGZS11G> update emp set sal=4000 where empno=7788;
1 row updated.

先不提交这个事务,在另外窗口新口Session,使用SYS用户查询相关信息,进行进一步分析研究。

2.获得事务信息:
从事务表中可以获得关于这个事务的信息,该事务位于6号回滚段(XIDUSN),在6号回滚段上,该事务位于第0号事务槽(XIDSLOT)

sys@TQGZS11G> select xidusn,xidslot,xidsqn,ubablk,ubafil,ubarec from v$transaction;
    XIDUSN    XIDSLOT     XIDSQN     UBABLK     UBAFIL     UBAREC
---------- ---------- ---------- ---------- ---------- ----------
         6          0        898      20650          3         23

V$ROLLSTAT视图中也可以获得事务信息,XACTS字段代表的是活动事务的数量,同样看到该事务位于6号回滚段:

sys@TQGZS11G> select usn,writes,rssize,xacts,hwmsize,shrinks,wraps from v$rollstat;
       USN     WRITES     RSSIZE      XACTS    HWMSIZE    SHRINKS      WRAPS
---------- ---------- ---------- ---------- ---------- ---------- ----------
         0       5408     385024          0     385024          0          0
         1      35358   52617216          0   52617216          0          0
         2      59510    2285568          0    2285568          0          1
         3      26232   61530112          0   61530112          0          0
         4      46336    2220032          0    2220032          0          0
         5      27252   38723584          0   38723584          0          0
         6      23784   31580160          1   31580160          0          0
         7      26116    3268608          0    3268608          0          0
         8      29098   40689664          0   40689664          0          0
         9      27214    1236992          0    1236992          0          0
        10      31534    2285568          0    2285568          0          0
11 rows selected.

3. 获得回滚段名称并转储段头信息:
查询V$ROLLNAME视图获得回滚段名称,并转储回滚段头信息:

sys@TQGZS11G> select * from v$rollname where usn=6;
       USN NAME
---------- --------------------------------------------------
         6 _SYSSMU6_1186132793$
sys@TQGZS11G> alter system dump undo header '_SYSSMU6_1186132793$';
System altered.

生成的跟踪文件如下:

sys@TQGZS11G> @gettrcname.sql      
TRACE_FILE
--------------------------------------------------------------------------------
/oracle/diag/rdbms/tqgzs11g/tqgzs11g/trace/tqgzs11g_ora_10642.trc

4. 获得跟踪文件信息:
注意这就是前边多次提到过的回滚段头的信息,其中包括事务表信息,从以下的跟踪文件中,可以清晰地看到这些内容:

********************************************************************************
Undo Segment:  _SYSSMU6_1186132793$ (6)
********************************************************************************
  Extent Control Header
  -----------------------------------------------------------------
  Extent Header:: spare1: 0      spare2: 0      #extents: 122    #blocks: 3855
                  last map  0x00000000  #maps: 0      offset: 4080
      Highwater::  0x00c050aa  ext#: 116    blk#: 33     ext size: 128
  #blocks in seg. hdr's freelists: 0
  #blocks below: 0
  mapblk  0x00000000  offset: 116
                   Unlocked
     Map Header:: next  0x00000000  #extents: 122  obj#: 0      flag: 0x40000000
  Extent Map
  -----------------------------------------------------------------
   0x00c0005a  length: 7
   0x00c07d01  length: 8
   0x00c07f09  length: 8
  <省略部分内容……>
   0x00c06289  length: 128
   0x00c06671  length: 8
   0x00c06709  length: 128

Retention Table
  -----------------------------------------------------------
Extent Number:0  Commit Time: 1248914085
Extent Number:1  Commit Time: 1248914087
Extent Number:2  Commit Time: 1248914087
Extent Number:3  Commit Time: 1248914087
  <省略部分内容……>
Extent Number:119  Commit Time: 1248914063
Extent Number:120  Commit Time: 1248914069
Extent Number:121  Commit Time: 1248914085

  TRN CTL:: seq: 0x02d5 chd: 0x0010 ctl: 0x0013 inc: 0x00000000 nfb: 0x0000
            mgc: 0xb000 xts: 0x0068 flg: 0x0001 opt: 2147483646 (0x7ffffffe)
            uba: 0x00c050aa.02d5.17 scn: 0x0000.0010603b
Version: 0x01
  FREE BLOCK POOL::
    uba: 0x00000000.02d5.16 ext: 0x74 spc: 0x14d6
    uba: 0x00000000.02d5.05 ext: 0x74 spc: 0xc48
    uba: 0x00000000.02d4.38 ext: 0x73 spc: 0x6f6
    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   10    0x80  0x0382  0x0074  0x0000.0010633d  0x00c050aa  0x0000.000.00000000  0x00000001   0x00000000  0
   0x01    9    0x00  0x0381  0x000b  0x0000.001061aa  0x00c050a9  0x0000.000.00000000  0x00000001   0x00000000  1
261838661
   0x02    9    0x00  0x0381  0x001a  0x0000.00106276  0x00c050aa  0x0000.000.00000000  0x00000002   0x00000000  1
261838662
  <省略后面内容……>

回顾前面的事务信息,该事务正好占用的是第0号事务槽(0x00),状态(state)为10代表是活动事务

5. 转储前镜像信息:
再来看DBA(Data Block Address),这个DBA指向的就是包含这个事务的前镜像的数据地址0x00c050aa。看一下这个地址如何换算:DBA代表数据块的存储地址,由10位文件号+22位数据块(Block)组成。将0x00c050aa转换为二进制就是:0000 0000 1100 0000 0101 0000 1010 1010。

前10位代表文件号为3,后22位代表Block号为20650。经过转换后,该前镜像信息位于file 3 block 20650。这与从事务表中查询得到的数据完全一致:

sys@TQGZS11G> select xidusn,xidslot,xidsqn,ubablk,ubafil,ubarec from v$transaction;
    XIDUSN    XIDSLOT     XIDSQN     UBABLK     UBAFIL     UBAREC
---------- ---------- ---------- ---------- ---------- ----------
        6          0        898      20650          3         23

提示:
很多深入研究的内容在数据库内部都有完整的体现,不过通常我们很少注意,只有将两者结合起来学习、研究和理解,我们才能深刻地理解到Oracle的本质。希望大家在阅读这部分内容的时候能够耐心、细致并有所收获。

为了同时说明一些其它内容,继续先前的SCOTT用户的事务,再更新2条记录:

scott@TQGZS11G> update emp set sal=4000 where empno=7788;
1 row updated.
scott@TQGZS11G> update emp set sal=4000 where empno=7782;
1 row updated.
scott@TQGZS11G> update emp set sal=4000 where empno=7698;
1 row updated.

然后将回滚段中的这个Block转储出来:

sys@TQGZS11G> alter system dump datafile 3 block 20650;
System altered.

这是跟踪文件开始部分的信息:

sys@TQGZS11G> @gettrcname.sql
TRACE_FILE
--------------------------------------------------------------------------------
/oracle/diag/rdbms/tqgzs11g/tqgzs11g/trace/tqgzs11g_ora_10642.trc

*** 2009-12-26 23:10:20.230
Start dump data blocks tsn: 2 file#:3 minblk 20650 maxblk 20650
Block dump from cache:
Dump of buffer cache at level 4 for tsn=2, rdba=12603562
BH (0x247e5adc) file#: 3 rdba: 0x00c050aa (3/20650) class: 28 ba: 0x24442000
  set: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0, 25 lid: 0x00000000,0x00000000
  dbwrid: 0 obj: -1 objn: 0 tsn: 2 afn: 3
  hash: [0x24bf433c,0x323ad5b4] lru: [0x247e50ac,0x247e4e3c]
  obj-flags: object_ckpt_list
  ckptq: [0x243f1380,0x25ff1d40] fileq: [0x327dc7cc,0x26ff4858] objq: [0x26ff480c,0x303aa140]
  st: XCURRENT md: NULL tch: 8
  flags: buffer_dirty block_written_once redo_since_read
          gotten_in_current_mode
  LRBA: [0x37.1378.0] LSCN: [0x0.106841] HSCN: [0x0.10684c] HSUB: [1]
  cr pin refcnt: 0 sh pin refcnt: 0
  buffer tsn: 2 rdba: 0x00c050aa (3/20650)
  scn: 0x0000.0010684c seq: 0x01 flg: 0x00 tail: 0x684c0201
  frmt: 0x02 chkval: 0x0000 type: 0x02=KTU UNDO BLOCK
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x24442000 to 0x24444000
24442000 0000A202 00C050AA 0010684C 00010000  [.....P..Lh......]
24442010 00000000 00000006 00000382 191902D5  [................]
24442020 1FE80000 1EE81F74 1DDC1E74 1CF01D78  [....t...t...x...]
<中间部分内容省略……>
24443FD0 C5480C10 B4AF0FAE 891FD92E FC98F37F  [..H.............]
24443FE0 1CA71084 28AEF9CD 11049EAA 5BB894EF  [.......(.......[]
24443FF0 2001136C 0C6D7807 182D081A 684C0201  [l.. .xm...-...Lh]

********************************************************************************
UNDO BLK:
xid: 0x0006.000.00000382  seq: 0x2d5 cnt: 0x19  irb: 0x19  icl: 0x0   flg: 0x0000

注意这部分信息中有一个参数irb: 0x19,irb指的是回滚段中记录的最近未提交变更开始之处,如果开始回滚,这是起始的搜索点。

接下来是回滚信息的偏移量,最后一个地址正好0x19的信息:

Rec Offset      Rec Offset      Rec Offset      Rec Offset      Rec Offset
---------------------------------------------------------------------------
0x01 0x1f74     0x02 0x1ee8     0x03 0x1e74     0x04 0x1ddc     0x05 0x1d78
0x06 0x1cf0     0x07 0x1bcc     0x08 0x1b64     0x09 0x1b0c     0x0a 0x1ab8
0x0b 0x1a5c     0x0c 0x19e8     0x0d 0x198c     0x0e 0x1938     0x0f 0x189c
0x10 0x1814     0x11 0x1788     0x12 0x1720     0x13 0x1690     0x14 0x1608
0x15 0x157c     0x16 0x1514     0x17 0x1490     0x18 0x1434     0x19 0x13d8

从接下来的信息中找到0x19信息:

*-----------------------------
* Rec #0x19  slt: 0x00  objn: 69515(0x00010f8b)  objd: 69515  tblspc: 4(0x00000004)
*       Layer:  11 (Row)   opc: 1   rci 0x18
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: 0x00c050aa.02d5.18
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0100001f  hdba: 0x0100001b
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 5(0x5) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 1
col  5: [ 3]  c2 1d 33

c2 1d 33转换为十进制就是2850(关于数字值的内部存储及转换方式请参考前面日志)。这是最后更新记录的前镜像,Oracle就是这样通过回滚段保留前镜像信息的:

update emp set sal=4000 where empno=7698;

注意在这条UNDO记录上,还记录一个数据rci,该参数代表的就是UNDO Chain(同一事务中的多次修改,根据Chain链接关联)的下一个偏移量,此处为rci 0x18。找到0x18这条UNDO记录:

*-----------------------------
* Rec #0x18  slt: 0x00  objn: 69515(0x00010f8b)  objd: 69515  tblspc: 4(0x00000004)
*       Layer:  11 (Row)   opc: 1   rci 0x17
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: 0x00c050aa.02d5.17
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0100001f  hdba: 0x0100001b
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 6(0x6) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 1
col  5: [ 3]  c2 19 33

这里记录的c2 19 33转换为十进制就是2450,是第二条更新的前镜像数据:

update emp set sal=4000 where empno=7782;

这里的rci指向下一条记录rci 0x17,找到0x17:

*-----------------------------
* Rec #0x17  slt: 0x00  objn: 69515(0x00010f8b)  objd: 69515  tblspc: 4(0x00000004)
*       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: 0x00c050aa.02d5.14 ctl max scn: 0x0000.00106005 prv tx scn: 0x0000.0010603b
txn start scn: scn: 0x0000.00000000 logon user: 81
prev brb: 12603560 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x03  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: Z
KDO Op code: URP row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x0100001f  hdba: 0x0100001b
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 7(0x7) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 0
Vector content:
col  5: [ 2]  c2 1f

这里c2 1f转换为十进制是3000,正是第一条更新的前镜像记录:

update emp set sal=4000 where empno=7788;

这是这个事务中最老(远)一条更新的数据,所以其UNDO Chain的指针为rci 0x00,表示这是最后一条记录,也可以从x$bh中找到这些数据块:

sys@TQGZS11G> select b.segment_name,a.file#,a.dbarfil,a.dbablk,a.class,a.state
  2  from x$bh a,dba_extents b where b.relative_fno=a.dbarfil
  3  and b.block_id <= a.dbablk and b.block_id + b.blocks > a.dbablk
  4  and b.owner='SCOTT' and b.segment_name='EMP';
SEGMENT_NAME              FILE#    DBARFIL     DBABLK      CLASS      STATE
-------------------- ---------- ---------- ---------- ---------- ----------
EMP                           4          4         31          1          1
EMP                           4          4         28          1          1
EMP                           4          4         30          1          1
EMP                           4          4         27          4          1
EMP                           4          4         32          1          1
EMP                           4          4         29          1          1
6 rows selected.

注意class为4的是段头,class为1、块号为31的为数据块。如果此时在其他进程查询scott.emp表,Oracle需要构造一致性读,通过前镜像把变化前的数据展现给用户:

sys@TQGZS11G> select * from scott.emp;
     EMPNO ENAME           JOB                    MGR HIREDATE               SAL       COMM     DEPTNO
---------- --------------- --------------- ---------- --------------- ---------- ---------- ----------
      7369 SMITH           CLERK                 7902 17-DEC-80              800                    20
      7499 ALLEN           SALESMAN              7698 20-FEB-81             1600        300         30
      7521 WARD            SALESMAN              7698 22-FEB-81             1250        500         30
      7566 JONES           MANAGER               7839 02-APR-81             2975                    20
      7654 MARTIN          SALESMAN              7698 28-SEP-81             1250       1400         30
      7698 BLAKE           MANAGER               7839 01-MAY-81             2850                    30
      7782 CLARK           MANAGER               7839 09-JUN-81             2450                    10
      7788 SCOTT           ANALYST               7566 19-APR-87             3000                    20
      7839 KING            PRESIDENT                  17-NOV-81             5000                    10
      7844 TURNER          SALESMAN              7698 08-SEP-81             1500          0         30
      7876 ADAMS           CLERK                 7788 23-MAY-87             1100                    20
      7900 JAMES           CLERK                 7698 03-DEC-81              950                    30
      7902 FORD            ANALYST               7566 03-DEC-81             3000                    20
      7934 MILLER          CLERK                 7782 23-JAN-82             1300                    10
14 rows selected.

再来查询:

sys@TQGZS11G> select b.segment_name,a.file#,a.dbarfil,a.dbablk,a.class,a.state,
  2  decode(bitand(flag,1),0,'N','Y') DIRTY
  3  from x$bh a,dba_extents b where b.relative_fno=a.dbarfil
  4  and b.block_id <= a.dbablk and b.block_id + b.blocks > a.dbablk
  5  and b.owner='SCOTT' and b.segment_name='EMP';
SEGMENT_NAME              FILE#    DBARFIL     DBABLK      CLASS      STATE DIR
-------------------- ---------- ---------- ---------- ---------- ---------- ---
EMP                           4          4         31          1          3 N
EMP                           4          4         31          1          3 N

EMP                           4          4         31          1          1 Y
EMP                           4          4         28          1          1 N
EMP                           4          4         30          1          1 N
EMP                           4          4         27          4          1 N
EMP                           4          4         32          1          1 N
EMP                           4          4         29          1          1 N
8 rows selected.

注意到此时,Buffer Cache中多出两个数据块,也就是31存在3份,其中STATE为3的就是一致性读构造的前镜像。

6. 转储数据块信息:
在前镜像信息中,Oracle还记录了前镜像对应的数据块地址,用户可以从bdba记录中获得这部分信息,以先前的一个数据为例,bdba: 0x0100001f记录了更改数据块的地址,0x0100001f经过转换为二进制就是:0000 0001 0000 0000 0000 0000 0001 1111,也正是file 4 block 31。

再将数据表中的Block转储出来,看看其中记录了什么样的信息:

sys@TQGZS11G> alter system dump datafile 4 block 31;
System altered.

检查跟踪文件,获取数据块信息:

Start dump data blocks tsn: 4 file#:4 minblk 31 maxblk 31

*** 2009-12-27 02:33:59.457
Block dump from cache:
Dump of buffer cache at level 4 for tsn=4, rdba=16777247
Block dump from disk:
buffer tsn: 4 rdba: 0x0100001f (4/31)
scn: 0x0000.0010982d seq: 0x01 flg: 0x04 tail: 0x982d0601
frmt: 0x02 chkval: 0x68eb type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00B34600 to 0x00B36600
B34600 0000A206 0100001F 0010982D 04010000  [........-.......]
B34610 000068EB 00010001 00010F8B 0010982D  [.h..........-...]
B34620 1FE80000 00321F02 01000019 00120005  [......2.........]
B34630 00000223 00C05A9A 00410170 00008000  [#....Z..p.A.....]
<中间内容省略……>
B365D0 0204C202 002C1FC1 4AC20308 4D530546  [......,....JF.SM]
B365E0 05485449 52454C43 50C2034B B4770703  [ITH.CLERK..P..w.]
B365F0 0101110C 09C20201 15C102FF 982D0601  [..............-.]
Block header dump:  0x0100001f
Object id on Block? Y
seg/obj: 0x10f8b  csc: 0x00.10982d  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/Fsc
0x01   0x0005.012.00000223  0x00c05a9a.0170.41  C---    0  scn 0x0000.0007eab2
0x02   0x0006.000.00000382  0x00c050aa.02d5.19  ----    3  fsc 0x0002.00000000
bdba: 0x0100001f
data_block_dump,data header at 0xb34664

这里存在ITL事务槽信息,ITL事务槽指Interested Transaction List(ITL),事务必须获得一个ITL事务槽才能够进行数据修改。ITL内容主要包括xid(Transaction ID)、Uba(Undo Block Address)和Lck(Lock Status)

xid=Undo.Segment.Number+Transaction.Table.Slot.Number+Wrap

在以上输出中,看到Itl2(0x02)上存在活动事务。将xid=0x0006.000.00000382分解一下:该事务指向6号回滚段,Slot号为0x00(转换为十进制正好是0),wrap#为0x0382,正是dump回滚段看到的那个事务

  index  state cflags  wrap#    uel         scn            dba            parent-xid    nub     stmt_num    cmt
  ------------------------------------------------------------------------------------------------
  0x00   10    0x80  0x0382  0x0074  0x0000.0010633d  0x00c050aa  0x0000.000.00000000  0x00000001   0x00000000  0

可以看到,在数据块上同样存在指向回滚段的事务信息。UBA代表的是Undo Block Address,指向具体的回滚段可以看到该ITL上Uba=0x00c050aa.02d5.19。将这个UBA进行分解,其中0x00c050aa正好是前镜像的地址,seq:02d5是顺序号,19是UNDO记录的开始地址(irb信息)

UBA的内容和UNDO中的信息完全相符:

UNDO BLK:
xid: 0x0006.000.00000382  seq: 0x2d5 cnt: 0x19  irb: 0x19  icl: 0x0   flg: 0x0000

继续向下可以找到这3条被修改的记录,锁定位信息LB指向0x2号ITL事务槽

tab 0, row 5, @0x1d11
tl: 40 fb: --H-FL-- lb: 0x2  cc: 8
col  0: [ 3]  c2 4d 63
col  1: [ 5]  42 4c 41 4b 45
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 05 01 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 1f
tab 0, row 6, @0x1d39
tl: 40 fb: --H-FL-- lb: 0x2  cc: 8
col  0: [ 3]  c2 4e 53
col  1: [ 5]  43 4c 41 52 4b
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 06 09 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 0b
tab 0, row 7, @0x1e4c
tl: 40 fb: --H-FL-- lb: 0x2  cc: 8
col  0: [ 3]  c2 4e 59
col  1: [ 5]  53 43 4f 54 54
col  2: [ 7]  41 4e 41 4c 59 53 54
col  3: [ 3]  c2 4c 43
col  4: [ 7]  77 bb 04 13 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 15

至此,整个事务过程被完全解析。最后总结一下这个事务的内部流程。

⑴ 首先当一个事务开始时,需要在回滚段事务表上分配一个事务槽。
⑵ 在数据块头部获取一个ITL事务槽,该事务槽指向回滚段头的事务槽。
⑶ 在修改数据之前,需要记录前镜像信息,这个信息以UNDO RECORD的形式存储在回滚段中,回滚段头事务槽指向该记录。
⑷ 锁定修改行,修改行锁定位(lb-lock byte)指向ITL事务槽。
⑸ 数据修改可以进行。

这就是一个事务的基本流程。

7. 块清除(Block Cleanouts):

当发出提交(commit)之后,Oracle怎样来处理。通过前面的日志可以知道,Oracle需要写出Redo来保证故障时数据可以被恢复;我们也知道Oracle并不需要在提交时就写出变更的数据块。那么在提交时,Oracle需要对数据块进行哪些操作呢?

回忆一下上文,可以知道,在事务需要修改数据时,必须分配ITL事务槽,必须锁定行,必须分配回滚段事务槽和回滚空间记录前镜像。当事务提交时,Oracle需要将回滚段上的事务表信息标记为非活动,以便空间可以重用;那么还有ITL事务信息和锁定信息需要清除,以记录提交

由于Oracle在数据块上存储了ITL和锁定等事务信息,所以Oracle必须在事务提交之后清除这些事务数据。这就是块清除块清除主要要清除的数据有行级锁和ITL信息(包括提交标志、SCN等)

如果提交时修改过的数据块仍然在Buffer Cache之中,那么Oracle可以清除ITL信息,这叫作快速块清除(Fast Block Cleanout),快速块清除还有一个限制,当修改的块数量超过Buffer Cache约10%,则对超出部分不再进行快速块清除。

如果提交事务的时候,修改过的数据块已经被写回到数据文件上(或大量修改超出Buffer Cache 10%的部分),再次读出该数据块进行修改,显然成本过于高昂,对于这种情况,Oracle选择延迟块清除(Delayed Block Cleanout)等到下一次访问该Block时再来清除ITL锁定信息,这就是延迟块清除。Oracle通过延迟块清除来提高数据库性能,加快提交操作。

快速提交是最普遍的情况,来看一下延迟块清除的处理。继续前面的测试:

scott@TQGZS11G> update emp set sal=4000 where empno=7788;
1 row updated.
scott@TQGZS11G> update emp set sal=4000 where empno=7782;
1 row updated.
scott@TQGZS11G> update emp set sal=4000 where empno=7698;
1 row updated.

更新完成之后,强制刷新Buffer Cache,将Buffer Cache中的数据都写出到数据文件:

sys@TQGZS11G> alter session set events = 'immediate trace name flush_cache';
Session altered.

此时再提交事务:

scott@TQGZS11G> commit;
Commit complete.

由于此时更新过的数据已经写出到数据文件,Oracle将执行延迟块清除,将此时的数据块和回滚段转储出来:

sys@TQGZS11G> alter system dump datafile 4 block 31;    --数据块
System altered.
sys@TQGZS11G> alter system dump undo header '_SYSSMU6_1186132793$';    --回滚段头
System altered.
sys@TQGZS11G> alter system dump datafile 3 block 20650;        --回滚段块
System altered.

研究一下,查看数据块上的信息,ITL事务信息仍然存在:

Start dump data blocks tsn: 4 file#:4 minblk 31 maxblk 31

*** 2009-12-27 03:18:33.727
Block dump from cache:
Dump of buffer cache at level 4 for tsn=4, rdba=16777247
Block dump from disk:
buffer tsn: 4 rdba: 0x0100001f (4/31)
scn: 0x0000.0010982d seq: 0x01 flg: 0x04 tail: 0x982d0601
frmt: 0x02 chkval: 0x68eb type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00B34600 to 0x00B36600
B34600 0000A206 0100001F 0010982D 04010000  [........-.......]
B34610 000068EB 00010001 00010F8B 0010982D  [.h..........-...]
B34620 1FE80000 00321F02 01000019 00120005  [......2.........]
<中间部分省略……>
B365D0 0204C202 002C1FC1 4AC20308 4D530546  [......,....JF.SM]
B365E0 05485449 52454C43 50C2034B B4770703  [ITH.CLERK..P..w.]
B365F0 0101110C 09C20201 15C102FF 982D0601  [..............-.]
Block header dump:  0x0100001f
Object id on Block? Y
seg/obj: 0x10f8b  csc: 0x00.10982d  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/Fsc
0x01   0x0005.012.00000223  0x00c05a9a.0170.41  C---    0  scn 0x0000.0007eab2
0x02   0x0006.000.00000382  0x00c050aa.02d5.19  ----    3  fsc 0x0002.00000000
bdba: 0x0100001f
data_block_dump,data header at 0xb34664

数据块的锁定信息仍然存在:

tab 0, row 5, @0x1d11
tl: 40 fb: --H-FL-- lb: 0x2  cc: 8
col  0: [ 3]  c2 4d 63
col  1: [ 5]  42 4c 41 4b 45
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 05 01 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 1f
tab 0, row 6, @0x1d39
tl: 40 fb: --H-FL-- lb: 0x2  cc: 8
col  0: [ 3]  c2 4e 53
col  1: [ 5]  43 4c 41 52 4b
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 06 09 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 0b
tab 0, row 7, @0x1e4c
tl: 40 fb: --H-FL-- lb: 0x2  cc: 8
col  0: [ 3]  c2 4e 59
col  1: [ 5]  53 43 4f 54 54
col  2: [ 7]  41 4e 41 4c 59 53 54
col  3: [ 3]  c2 4c 43
col  4: [ 7]  77 bb 04 13 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 15

再来看回滚段的信息:

  index  state cflags  wrap#    uel         scn            dba            parent-xid    nub     stmt_num    cmt
  ------------------------------------------------------------------------------------------------
   0x00    9    0x00  0x0382  0x0010  0x0000.0010b963  0x00c050aa  0x0000.000.00000000  0x00000001   0x00000000  1261854823

事务提交,事务表已经释放。如果此时查询SCOTT.EMP表,数据库将产生延迟块清除:

sys@TQGZS11G> set autotrace on
sys@TQGZS11G> select * from scott.emp;
     EMPNO ENAME           JOB                    MGR HIREDATE               SAL       COMM     DEPTNO
---------- --------------- --------------- ---------- --------------- ---------- ---------- ----------
      7369 SMITH           CLERK                 7902 17-DEC-80              800                    20
      7499 ALLEN           SALESMAN              7698 20-FEB-81             1600        300         30
      7521 WARD            SALESMAN              7698 22-FEB-81             1250        500         30
      7566 JONES           MANAGER               7839 02-APR-81             2975                    20
      7654 MARTIN          SALESMAN              7698 28-SEP-81             1250       1400         30
      7698 BLAKE           MANAGER               7839 01-MAY-81             4000                    30
      7782 CLARK           MANAGER               7839 09-JUN-81             4000                    10
      7788 SCOTT           ANALYST               7566 19-APR-87             4000                    20
      7839 KING            PRESIDENT                  17-NOV-81             5000                    10
      7844 TURNER          SALESMAN              7698 08-SEP-81             1500          0         30
      7876 ADAMS           CLERK                 7788 23-MAY-87             1100                    20
      7900 JAMES           CLERK                 7698 03-DEC-81              950                    30
      7902 FORD            ANALYST               7566 03-DEC-81             3000                    20
      7934 MILLER          CLERK                 7782 23-JAN-82             1300                    10
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    14 |   518 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| EMP  |    14 |   518 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          9  consistent gets
          6  physical reads
        116  redo size

       1420  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         14  rows processed

注意到查询在此时产生了物理读和Redo,这个Redo就是因为延迟块清除导致的。再次查询,则不会继续生成Redo了:

sys@TQGZS11G> /
     EMPNO ENAME           JOB                    MGR HIREDATE               SAL       COMM     DEPTNO
---------- --------------- --------------- ---------- --------------- ---------- ---------- ----------
      7369 SMITH           CLERK                 7902 17-DEC-80              800                    20
      7499 ALLEN           SALESMAN              7698 20-FEB-81             1600        300         30
……
14 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    14 |   518 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| EMP  |    14 |   518 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          8  consistent gets
          0  physical reads
          0  redo size

       1420  bytes sent via SQL*Net to client
        420  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         14  rows processed

再次转储一下该Block来看看此时数据库块上的信息:

sys@TQGZS11G> alter system dump datafile 4 block 31;    --数据块
System altered.

看到此时ITL事务信息已经清除,但是注意,这里的Xid和Uba信息仍然存在

Start dump data blocks tsn: 4 file#:4 minblk 31 maxblk 31
Block dump from cache:
Dump of buffer cache at level 4 for tsn=4, rdba=16777247
BH (0x25bf891c) file#: 4 rdba: 0x0100001f (4/31) class: 1 ba: 0x25b2a000
  set: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0, 25 lid: 0x00000000,0x00000000
  dbwrid: 0 obj: 69515 objn: 69515 tsn: 4 afn: 4
  hash: [0x323a7804,0x323a7804] lru: [0x257f64ec,0x253f60dc]
  ckptq: [NULL] fileq: [NULL] objq: [0x253f613c,0x303c4140]
  st: XCURRENT md: NULL tch: 2
  flags: only_sequential_access block_written_once redo_since_read
  LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
  cr pin refcnt: 0 sh pin refcnt: 0
  buffer tsn: 4 rdba: 0x0100001f (4/31)
  scn: 0x0000.0010bca8 seq: 0x01 flg: 0x04 tail: 0xbca80601
  frmt: 0x02 chkval: 0x771a type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x25B2A000 to 0x25B2C000
25B2A000 0000A206 0100001F 0010BCA8 04010000  [................]
25B2A010 0000771A 00010001 00010F8B 0010BCA8  [.w..............]
25B2A020 1FE80000 00321F02 01000019 00120005  [......2.........]
<中间部分省略……>
25B2BFD0 0204C202 002C1FC1 4AC20308 4D530546  [......,....JF.SM]
25B2BFE0 05485449 52454C43 50C2034B B4770703  [ITH.CLERK..P..w.]
25B2BFF0 0101110C 09C20201 15C102FF BCA80601  [................]
Block header dump:  0x0100001f
Object id on Block? Y
seg/obj: 0x10f8b  csc: 0x00.10bca8  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/Fsc
0x01   0x0005.012.00000223  0x00c05a9a.0170.41  C---    0  scn 0x0000.0007eab2
0x02   0x0006.000.00000382  0x00c050aa.02d5.19  C---    0  scn 0x0000.0010b963
bdba: 0x0100001f
data_block_dump,data header at 0x25b2a064

数据行的锁定位也已经清除:

sys@TQGZS11G> alter system dump undo header '_SYSSMU6_1186132793$';    --回滚段头
System altered.

tab 0, row 5, @0x1d11
tl: 40 fb: --H-FL-- lb: 0x0  cc: 8
col  0: [ 3]  c2 4d 63
col  1: [ 5]  42 4c 41 4b 45
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 05 01 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 1f
tab 0, row 6, @0x1d39
tl: 40 fb: --H-FL-- lb: 0x0  cc: 8
col  0: [ 3]  c2 4e 53
col  1: [ 5]  43 4c 41 52 4b
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 06 09 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 0b
tab 0, row 7, @0x1e4c
tl: 40 fb: --H-FL-- lb: 0x0  cc: 8
col  0: [ 3]  c2 4e 59
col  1: [ 5]  53 43 4f 54 54
col  2: [ 7]  41 4e 41 4c 59 53 54
col  3: [ 3]  c2 4c 43
col  4: [ 7]  77 bb 04 13 01 01 01
col  5: [ 2]  c2 29
col  6: *NULL*
col  7: [ 2]  c1 15

8. 提交之后的UNDO信息:
当提交事务之后,回滚段事务表标记事务为非活动,继续再来看一下回滚段数据块的信息。可以看到这里irb指向了0x3a,此前的事务已经不可回滚。

Start dump data blocks tsn: 2 file#:3 minblk 20650 maxblk 20650
Block dump from cache:
Dump of buffer cache at level 4 for tsn=2, rdba=12603562
BH (0x247e5adc) file#: 3 rdba: 0x00c050aa (3/20650) class: 28 ba: 0x24442000
  set: 3 bsz: 8192 bsi: 0 sflg: 0 pwc: 0, 25 lid: 0x00000000,0x00000000
  dbwrid: 0 obj: -1 objn: 0 tsn: 2 afn: 3
  hash: [0x323ad5b4,0x323ad5b4] lru: [0x257e7aec,0x24bf30ec]
  lru-flags: on_auxiliary_list
  ckptq: [NULL] fileq: [NULL] objq: [NULL]
  st: FREE md: NULL tch: 0 lfb: 33
  flags:
  cr pin refcnt: 0 sh pin refcnt: 0
  Buffer contents not dumped
Block dump from disk:
buffer tsn: 2 rdba: 0x00c050aa (3/20650)
scn: 0x0000.0010bb59 seq: 0x03 flg: 0x04 tail: 0xbb590203
frmt: 0x02 chkval: 0xc30c type: 0x02=KTU UNDO BLOCK
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00BEA600 to 0x00BEC600
BEA600 0000A202 00C050AA 0010BB59 04030000  [.....P..Y.......]
BEA610 0000C30C 00010006 00000382 3A3A02D5  [..............::]
BEA620 1FE80000 1EE81F74 1DDC1E74 1CF01D78  [....t...t...x...]
<中间部分省略……>
********************************************************************************
UNDO BLK:
xid: 0x0006.001.00000382  seq: 0x2d5 cnt: 0x3a  irb: 0x3a  icl: 0x0   flg: 0x0000

偏移量列表也已经新增到信息0x3a 0x0198:

Rec Offset      Rec Offset      Rec Offset      Rec Offset      Rec Offset
---------------------------------------------------------------------------
0x01 0x1f74     0x02 0x1ee8     0x03 0x1e74     0x04 0x1ddc     0x05 0x1d78
0x06 0x1cf0     0x07 0x1bcc     0x08 0x1b64     0x09 0x1b0c     0x0a 0x1ab8
0x0b 0x1a5c     0x0c 0x19e8     0x0d 0x198c     0x0e 0x1938     0x0f 0x189c
0x10 0x1814     0x11 0x1788     0x12 0x1720     0x13 0x1690     0x14 0x1608
0x15 0x157c     0x16 0x1514     0x17 0x1490     0x18 0x1434     0x19 0x13d8
0x1a 0x1350     0x1b 0x12f4     0x1c 0x126c     0x1d 0x11f0     0x1e 0x1188
0x1f 0x1100     0x20 0x1084     0x21 0x101c     0x22 0x0f34     0x23 0x0ecc
0x24 0x0e74     0x25 0x0de4     0x26 0x0d90     0x27 0x0cf4     0x28 0x0c48
0x29 0x0b9c     0x2a 0x0af0     0x2b 0x0a44     0x2c 0x0998     0x2d 0x08ec
0x2e 0x0840     0x2f 0x0790     0x30 0x06e0     0x31 0x0630     0x32 0x05c4
0x33 0x0558     0x34 0x04a8     0x35 0x043c     0x36 0x03d0     0x37 0x0340
0x38 0x0258     0x39 0x01f0     0x3a 0x0198

至于前镜像0x17、0x18、0x19的信息,仍然存在:

*-----------------------------
* Rec #0x17  slt: 0x00  objn: 69515(0x00010f8b)  objd: 69515  tblspc: 4(0x00000004)
*       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: 0x00c050aa.02d5.14 ctl max scn: 0x0000.00106005 prv tx scn: 0x0000.0010603b
txn start scn: scn: 0x0000.00000000 logon user: 81
prev brb: 12603560 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x03  ver: 0x01
compat bit: 4 (post-11) padding: 1
op: Z
KDO Op code: URP row dependencies Disabled
  xtype: XAxtype KDO_KDOM2 flags: 0x00000080  bdba: 0x0100001f  hdba: 0x0100001b
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 7(0x7) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 0
Vector content:
col  5: [ 2]  c2 1f

*-----------------------------
* Rec #0x18  slt: 0x00  objn: 69515(0x00010f8b)  objd: 69515  tblspc: 4(0x00000004)
*       Layer:  11 (Row)   opc: 1   rci 0x17
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: 0x00c050aa.02d5.17
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0100001f  hdba: 0x0100001b
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 6(0x6) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 1
col  5: [ 3]  c2 19 33

*-----------------------------
* Rec #0x19  slt: 0x00  objn: 69515(0x00010f8b)  objd: 69515  tblspc: 4(0x00000004)
*       Layer:  11 (Row)   opc: 1   rci 0x18
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: 0x00c050aa.02d5.18
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x0100001f  hdba: 0x0100001b
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 5(0x5) flag: 0x2c lock: 0 ckix: 0
ncol: 8 nnew: 1 size: 1
col  5: [ 3]  c2 1d 33

可以猜想,虽然这个事务已经提交,不可以回滚了,但是在覆盖之前,这个前镜像信息仍然存在,通过某种手段,我们应该仍然可以获得这个信息。这个猜想显然是成立的。

- The End -