作者: |【转载时请以超链接形式标明文章和信息】
链接:
回滚机制的深入研究:
如果大家有兴趣深入了解一下回滚段的机制,那么请跟随我将前面的例子进一步深化。
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.0010b963bdba: 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
可以猜想,虽然这个事务已经提交,不可以回滚了,但是在覆盖之前,这个前镜像信息仍然存在,通过某种手段,我们应该仍然可以获得这个信息。这个猜想显然是成立的。