说明:
本文通过dump data block、undo segment header block、undo block,来理解Oracle数据库中事物槽、UNDO等工作机制。
数据库版本:
Oracle 11.2.0.4.0
创建测试数据:
create table t1(id int,name char(10));
Table created.
create table t2(id int,name char(10)) INITRANS 5 MAXTRANS 100;
Table created.
create table t3(id int,name char(10)) INITRANS 5 MAXTRANS 256;
ERROR at line 1:
ORA-02209: invalid MAXTRANS option value
[oracle@cjc-db-01 ~]$ oerr ora 02209
02209, 00000, "invalid MAXTRANS option value"
// *Cause: The MAXTRANS value is not an integer between 1 and 255
// and greater than or equal to the INITRANS value.
// *Action: Choose a valid MAXTRANS value.
一个表,有1-255个事务槽,MAX_TRANS值最大255,建表时可以修改INI_TRANS。
SQL> SELECT TABLE_NAME,INI_TRANS,MAX_TRANS FROM DBA_TABLES WHERE TABLE_NAME IN ('T1','T2');
TABLE_NAME INI_TRANS MAX_TRANS
------------------------------ ---------- ----------
T1 1 255
T2 5 255
插入测试数据
insert into t1 values(1,'a');
insert into t1 values(2,'b');
insert into t1 values(3,'c');
insert into t1 values(4,'d');
insert into t1 values(5,'e');
commit;
查看 t1 表数据存储的数据文件号和块号:6号文件,15 block
select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid),id from t1;
DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) ID
------------------------------------ ------------------------------------ ----------
6 15 1
6 15 2
6 15 3
6 15 4
6 15 5
查看 6 号数据文件
col file_name for a50
select file_id,file_name from dba_data_files where file_id=6;
FILE_ID FILE_NAME
---------- --------------------------------------------------
6 /oracle/app/oracle/oradata/cjc/cjc_data01.dbf
查看UNDO和回滚段等信息
SQL> show parameter undo_tablespace
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_tablespace string UNDOTBS1
SQL> select * from v$rollname;
USN NAME
---------- ------------------------------
0 SYSTEM
1 _SYSSMU1_3724004606$
2 _SYSSMU2_2996391332$
3 _SYSSMU3_1723003836$
4 _SYSSMU4_1254879796$
5 _SYSSMU5_898567397$
6 _SYSSMU6_1263032392$
7 _SYSSMU7_2070203016$
8 _SYSSMU8_517538920$
9 _SYSSMU9_1650507775$
10 _SYSSMU10_1197734989$
11 rows selected
更新t1表数据,不提交,值由c更新为xxxxx;
update t1 set name='xxxxx' where id=3;
查看事务信息,记录在3号文件,482 block,2号rollname,18号slot
SQL> select ubafil,ubablk,xidusn,xidslot,xidsqn,start_scnb from v$transaction;
UBAFIL UBABLK XIDUSN XIDSLOT XIDSQN START_SCNB
---------- ---------- ---------- ---------- ---------- ----------
3 482 2 18 978 0
字段说明:
UBAFIL:
Undo block address (UBA) filenum,3号数据文件,也就是undotbs01.dbf;
UBABLK:
UBA block number
XIDUSN:
Undo segment number,也就是2号rollname(_SYSSMU2_2996391332$);
XIDSLOT:
Slot number
XIDSQN:
Sequence number
START_SCNB:
Start system change number (SCN) base
SQL> select file_id,file_name from dba_data_files where file_id=3;
FILE_ID FILE_NAME
---------- --------------------------------------------------
3 /oracle/app/oracle/oradata/cjc/undotbs01.dbf
查看对象ID
SQL> select object_id from dba_objects where object_name='T1';
OBJECT_ID
----------
88751
从上面查询信息,可以知道:
回滚块:3号文件,482 block
数据块:6文件,15 block
回滚段名称:_SYSSMU2_2996391332$
表CJC.T1:OBJECT_ID=88751
通过dump查看相关事务信息:
1.dump 回滚段头块:
---22432
select spid from v$process where addr in (select paddr from v$session where sid = (select sid from v$mystat where rownum=1));
alter system dump undo header '_SYSSMU2_2996391332$';
2.dump 回滚块:
---23222
select spid from v$process where addr in (select paddr from v$session where sid = (select sid from v$mystat where rownum=1));
alter system dump datafile 3 block 482;
3.dump 数据块:
---23281
select spid from v$process where addr in (select paddr from v$session where sid = (select sid from v$mystat where rownum=1));
alter system dump datafile 6 block 15;
查看dump trace:
SQL> show parameter user_dump_dest
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
user_dump_dest string /oracle/app/oracle/diag/rdbms/cjc/cjc/trace
一:分析回滚段头块
vi /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_22432.trc
********************************************************************************
Undo Segment: _SYSSMU2_2996391332$ (2)
********************************************************************************
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 6 #blocks: 47
last map 0x00000000 #maps: 0 offset: 4080
Highwater:: 0x00c001e2 ext#: 3 blk#: 2 ext size: 8
其中:
_SYSSMU2_2996391332$ (2) 表示是2号段;
extents: 6 表示有6个extent区(每个extent 8个block,一共48个block);
blocks: 47 表示2号回滚段6个区中有47个可用undo block(去掉了一个undo segment header block);
ext#: 3 表示事务发生在第3个区上(从0开始);
blk#: 2 表示事务发生在2号回滚段第3个区的第2个block上;
ext size: 8 表示1个区有8个undo block;
查询:可以看到,EXTENT_ID=3,对应的BLOCK_ID=480,第2个block是482,和之前v$transaction查询一致。
select extent_id,file_id,block_id,blocks,bytes from dba_extents where segment_name='_SYSSMU2_2996391332$';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS BYTES
---------- ---------- ---------- ---------- ----------
0 3 144 8 65536
1 3 296 8 65536
2 3 472 8 65536
3 3 480 8 65536
4 3 464 8 65536
5 3 280 8 65536
6 rows selected.
继续查看 Extent Map
Extent Map
-----------------------------------------------------------------
0x00c00091 length: 7
0x00c00128 length: 8
0x00c001d8 length: 8
0x00c001e0 length: 8
0x00c001d0 length: 8
0x00c00118 length: 8
2号undo 回滚段,一共8个extent,第一个区 7个block,其他区8个block。
继续看 Retention Table
Retention Table
-----------------------------------------------------------
Extent Number:0 Commit Time: 1721451627
Extent Number:1 Commit Time: 1721451627
Extent Number:2 Commit Time: 1721451627
Extent Number:3 Commit Time: 1721451627
Extent Number:4 Commit Time: 1721451627
Extent Number:5 Commit Time: 1721451627
区 commit time时间:转为位时间是1970-01-21。
select
to_char(to_date('1970-01-01','yyyy-mm-dd')+(1721451627/(3600*24*1000))+TO_NUMBER(SUBSTR(TZ_OFFSET(sessiontimezone), 1, 3))/24,'YYYY-MM-DD') datetime
from dual;
DATETIME
----------
1970-01-21
继续看 TRN CTL(事务控制)
TRN CTL:: seq: 0x0119 chd: 0x0007 ctl: 0x0001 inc: 0x00000000 nfb: 0x0000
mgc: 0xb000 xts: 0x0068 flg: 0x0001 opt: 2147483646 (0x7ffffffe)
uba: 0x00c001e2.0119.37 scn: 0x0000.0012e861
说明:
seq: 0x0119
表示此事务修改前的值所在的UNDO BLOCK块被覆盖了281次(16进制的0119转换为10进制是281),与下面的uba: 0x00c001e2.0119.37中的0119对应。
chd: 0x0007
表示发生一个新的事务,此时会在下面的TRNTBL::(事务表)的index=0x0007槽中放入新事务信息,即事务表的链头或叫入口。
ctl: 0x0001
表示事务表的链尾(实际上可以去TRN TBL::看index=0x0001,它对应的SCN=0x0000.0012f0d2是本事务表中最大的SCN,即此事务槽最后才会被覆盖)
nfb: 0x0000
表示UNDO块在空闲池的空闲块数,0x0000表示池中有0个空闲UNDO块,即FREE BLOCKPOOL::有0个空闲的块。
flg: 0x0001
表示该块的用途,1=KTUUNDO HEADER(2=KTU UNDO BLOCK等等)
uba: 0x00c001e2.0119.37
表示新事务的第一条UNDO记录(由三部分组成:
undo块的地址、UNDO块被重用的次数、在UNDO块的第几条记录)
继续看 FREE BLOCK POOL
FREE BLOCK POOL::
uba: 0x00000000.0119.36 ext: 0x3 spc: 0x3ae
uba: 0x00000000.0119.1b ext: 0x3 spc: 0xc58
uba: 0x00000000.00e0.2a ext: 0x2d spc: 0xe46
uba: 0x00000000.0000.00 ext: 0x0 spc: 0x0
uba: 0x00000000.0000.00 ext: 0x0 spc: 0x0
UNDO块的空闲池,当事务提交后,会把此事务所在的UNDO块加入空闲池中;
uba: 由三部分组成undo块的地址、UNDO块被重用的次数、在UNDO块的第几条记录,当undo块的地址为0说明UNDO块不是空闲的,即0x00000000;
ext: UNDO块是在哪个区(extent);
spc:UNDO块中多少空闲空间,单位字节,例如16进制的3ae转换为10进制是942,从上面的UNDO空闲池中看,有空闲的UNDO块。
继续看事物表 TRN TBL
TRN TBL::
index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x00 9 0x00 0x03d3 0x0017 0x0000.0012ef89 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 1721463352
0x01 9 0x00 0x03d3 0xffff 0x0000.0012f0d2 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 1721463820
0x02 9 0x00 0x03d2 0x0008 0x0000.0012ed77 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x03 9 0x00 0x03d2 0x0004 0x0000.0012e9f3 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721452827
0x04 9 0x00 0x03d2 0x0010 0x0000.0012e9fd 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721452827
0x05 9 0x00 0x03d2 0x000f 0x0000.0012ed4f 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x06 9 0x00 0x03d3 0x0020 0x0000.0012ee99 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721463217
0x07 9 0x00 0x03d2 0x0014 0x0000.0012e86b 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721451926
0x08 9 0x00 0x03d2 0x001d 0x0000.0012eda4 0x00000000 0x0000.000.00000000 0x00000000 0x00000000 1721463212
0x09 9 0x00 0x03d2 0x001b 0x0000.0012e889 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721451926
0x0a 9 0x00 0x03d2 0x0003 0x0000.0012e9a1 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721452621
0x0b 9 0x00 0x03d3 0x000e 0x0000.0012efee 0x00000000 0x0000.000.00000000 0x00000000 0x00000000 1721463365
0x0c 9 0x00 0x03d2 0x0019 0x0000.0012ed2f 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x0d 9 0x00 0x03d2 0x0005 0x0000.0012ed43 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x0e 9 0x00 0x03d3 0x0001 0x0000.0012f05a 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 1721463527
0x0f 9 0x00 0x03d2 0x001c 0x0000.0012ed5b 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x10 9 0x00 0x03d2 0x0015 0x0000.0012ebb0 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454027
0x11 9 0x00 0x03d1 0x0009 0x0000.0012e87f 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721451926
0x12 10 0x80 0x03d2 0x0003 0x0000.00000000 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 0
0x13 9 0x00 0x03d1 0x000a 0x0000.0012e8f7 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721452207
0x14 9 0x00 0x03d2 0x0011 0x0000.0012e875 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721451926
0x15 9 0x00 0x03d2 0x0016 0x0000.0012ec57 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454422
0x16 9 0x00 0x03d2 0x001a 0x0000.0012ecb2 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454594
0x17 9 0x00 0x03d2 0x000b 0x0000.0012efc1 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 1721463364
0x18 9 0x00 0x03d2 0x001f 0x0000.0012eec4 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721463217
0x19 9 0x00 0x03d2 0x000d 0x0000.0012ed39 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x1a 9 0x00 0x03d2 0x000c 0x0000.0012ed24 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x1b 9 0x00 0x03d1 0x0013 0x0000.0012e8f3 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721452207
0x1c 9 0x00 0x03d2 0x0002 0x0000.0012ed68 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454853
0x1d 9 0x00 0x03d2 0x001e 0x0000.0012edb2 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721463212
0x1e 9 0x00 0x03d2 0x0006 0x0000.0012edc8 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721463213
0x1f 9 0x00 0x03d2 0x0021 0x0000.0012ef25 0x00c001e2 0x0000.000.00000000 0x00000002 0x00000000 1721463338
0x20 9 0x00 0x03d1 0x0018 0x0000.0012eead 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721463217
0x21 9 0x00 0x03d2 0x0000 0x0000.0012ef51 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 1721463338
EXT TRN CTL::
usn: 2
sp1:0x00000000 sp2:0x00000000 sp3:0x00000000 sp4:0x00000000
sp5:0x00000000 sp6:0x00000000 sp7:0x00000000 sp8:0x00000000
TRN TBL::(事务表)是UNDO段头块最重要的部分。
字段说明:
index
表示事务表中槽号,是一个序列,从0x00开始到0x21结束,11g的版本有34个槽。
state
表示事务状态:9代表事务不活动,10代表事务正在活动,从这里我们看出16进制第index=0x12号槽上的事务正在活动。在发生事务前,Oracle会找事务控制列表中的chd=0x0007(从前面的TRN CTL得知),就是从index=0x07,存放当前最新的事务;
cflags
表示正在使用事务槽的事务的状态:0x00表示非活动事务、0x80表示活动事务、0x10表示死事务、0x90表示被回滚的死事务。
wrap#
表示事务表上的事务槽被重用的次数,它是XID的一部分。0x0003 表示此时事务槽被重用了3次。
uel
表示当前活动事务所在事务槽的下一个事务槽的指针(即如果又发生一个新的事务,此时就会用到UEL指向的事务槽上的index)。
scn
表示务事启动、提交、回滚的SCN.
dba
表示uba:第一部分的undo块地址,这个DBA是(rollback)回滚的起始点,是记录事务修改的最后一条记录所在UNDO块的地址。
nub
表示当前事务所用到的UNDO块的个数。
cmt
表示最接近当前的提交时间戳,是从1970年1月1号零晨开始的(以秒为单位记录)。
二:分析回滚块
vi /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23222.trc
Start dump data blocks tsn: 2 file#:3 minblk 482 maxblk 482
Block dump from cache:
Dump of buffer cache at level 4 for tsn=2 rdba=12583394
BH (0xbffed1d8) file#: 3 rdba: 0x00c001e2 (3/482) class: 20 ba: 0xbfe12000
set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 2 pwc: 335,19
dbwrid: 0 obj: -1 objn: 0 tsn: 2 afn: 3 hint: f
hash: [0xd9f0d9f0,0xd9f0d9f0] lru: [0xbffed538,0xbffec2f0]
ckptq: [NULL] fileq: [NULL] objq: [0xbbff0008,0xd420c608] objaq: [0xbfff1740,0xbffec328]
st: XCURRENT md: NULL fpin: 'ktuwh03: ktugnb' tch: 6
flags: block_written_once redo_since_read
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
Block dump from disk:
buffer tsn: 2 rdba: 0x00c001e2 (3/482)
scn: 0x0000.0012f10c seq: 0x01 flg: 0x04 tail: 0xf10c0201
frmt: 0x02 chkval: 0x2dd5 type: 0x02=KTU UNDO BLOCK
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007FFFF4E4DA00 to 0x00007FFFF4E4FA00
...
********************************************************************************
UNDO BLK:
xid: 0x0002.012.000003d2 seq: 0x119 cnt: 0x37 irb: 0x37 icl: 0x0 flg: 0x0000
Rec Offset Rec Offset Rec Offset Rec Offset Rec Offset
---------------------------------------------------------------------------
0x01 0x1f94 0x02 0x1f40 0x03 0x1eec 0x04 0x1e0c 0x05 0x1da4
0x06 0x1d3c 0x07 0x1cd4 0x08 0x1c08 0x09 0x1bac 0x0a 0x1b54
0x0b 0x1afc 0x0c 0x1a2c 0x0d 0x19d0 0x0e 0x1978 0x0f 0x1920
0x10 0x1880 0x11 0x181c 0x12 0x1788 0x13 0x1720 0x14 0x16b8
0x15 0x15c8 0x16 0x1560 0x17 0x14a8 0x18 0x1440 0x19 0x13dc
0x1a 0x1378 0x1b 0x1254 0x1c 0x115c 0x1d 0x10f0 0x1e 0x1058
0x1f 0x0fec 0x20 0x0f54 0x21 0x0e94 0x22 0x0dac 0x23 0x0d50
0x24 0x0cc8 0x25 0x0c6c 0x26 0x0be4 0x27 0x0b3c 0x28 0x0ab8
0x29 0x0a50 0x2a 0x09a0 0x2b 0x0950 0x2c 0x0870 0x2d 0x07e8
0x2e 0x06f4 0x2f 0x06a4 0x30 0x0634 0x31 0x05f0 0x32 0x05ac
0x33 0x0568 0x34 0x0524 0x35 0x049c 0x36 0x042c 0x37 0x0380
...
uba: 0x00c001e2.0119.2e ctl max scn: 0x0000.0012e7bf prv tx scn: 0x0000.0012e7d2
txn start scn: scn: 0x0000.0012efeb logon user: 102
prev brb: 12583390 prev bcl: 0
*-----------------------------
* Rec #0x37 slt: 0x12 objn: 88751(0x00015aaf) objd: 88751 tblspc: 9(0x00000009)
* 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: 0x00c001e2.0119.35 ctl max scn: 0x0000.0012e857 prv tx scn: 0x0000.0012e861
txn start scn: scn: 0x0000.00000000 logon user: 102
prev brb: 12583392 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: 2(0x2) 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: 0x0180000f hdba: 0x0180000a
itli: 2 ispac: 0 maxfr: 4858
vect = 3
col 1: [10] 63 20 20 20 20 20 20 20 20 20
其中:col 1: [10] 63 20 20 20 20 20 20 20 20 20 表示第3行第2列(name)修改前的值:
可以通过utl_raw.cast_to_varchar2转换:
SQL> SELECT utl_raw.cast_to_varchar2(replace('63 20 20 20 20 20 20 20 20 20',' ')) value FROM dual;
VALUE
--------------------------------------------------------------------------------
c
记录修改之前的列值 NAME=‘C’;
三:分析数据块
vi /oracle/app/oracle/diag/rdbms/cjc/cjc/trace/cjc_ora_23281.trc
Start dump data blocks tsn: 9 file#:6 minblk 15 maxblk 15
Block dump from cache:
Dump of buffer cache at level 4 for tsn=9 rdba=25165839
BH (0xbdf91198) file#: 6 rdba: 0x0180000f (6/15) class: 1 ba: 0xbd4a2000
set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 2 pwc: 335,19
dbwrid: 0 obj: 88751 objn: 88751 tsn: 9 afn: 6 hint: f
hash: [0xbdff3a28,0xd9833a70] lru: [0xbdf913c0,0xbdf91150]
ckptq: [NULL] fileq: [NULL] objq: [0xbdff3bc0,0xd4254bb0] objaq: [0xbdff3bd0,0xd4254ba0]
st: XCURRENT md: NULL fpin: 'ktspbwh2: ktspfmdb' tch: 1
flags: block_written_once redo_since_read
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
BH (0xbdff3970) file#: 6 rdba: 0x0180000f (6/15) class: 1 ba: 0xbdebc000
set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 2 pwc: 335,19
dbwrid: 0 obj: 88751 objn: 88751 tsn: 9 afn: 6 hint: f
hash: [0xd9833a70,0xbdf91250] lru: [0xd8103938,0xbffe6788]
lru-flags: moved_to_tail
ckptq: [NULL] fileq: [NULL] objq: [NULL] objaq: [NULL]
st: CR md: NULL fpin: 'ktspbwh2: ktspfmdb' tch: 1
cr: [scn: 0x0.12f104],[xid: 0x0.0.0],[uba: 0x0.0.0],[cls: 0x0.12f104],[sfl: 0x0],[lc: 0x0.12f05a]
flags: block_written_once redo_since_read
Block dump from disk:
buffer tsn: 9 rdba: 0x0180000f (6/15)
scn: 0x0000.0012f10c seq: 0x01 flg: 0x04 tail: 0xf10c0601
frmt: 0x02 chkval: 0x4093 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x00007FFFF4E4DA00 to 0x00007FFFF4E4FA00
Dump of memory from 0x00007FFFF4E4DA00 to 0x00007FFFF4E4FA00
.....
Repeat 497 times
7FFFF4E4F9A0 00000000 2C000000 C1020200 20650A06 [.......,......e ]
7FFFF4E4F9B0 20202020 20202020 0202002C 640A05C1 [ ,......d]
7FFFF4E4F9C0 20202020 20202020 02022C20 0A04C102 [ ,......]
7FFFF4E4F9D0 78787878 20202078 002C2020 03C10202 [xxxxx ,.....]
7FFFF4E4F9E0 2020620A 20202020 2C202020 C1020200 [.b ,....]
7FFFF4E4F9F0 20610A02 20202020 20202020 F10C0601 [..a ....]
Block header dump: 0x0180000f
可以看到name列值:a,b,xxxxx,d,e;
数据块内的事务槽信息:
Block header dump: 0x0180000f
Object id on Block? Y
seg/obj: 0x15aaf csc: 0x00.12f105 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1800008 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0002.00e.000003d3 0x00c001e2.0119.34 C--- 0 scn 0x0000.0012f05a
0x02 0x0002.012.000003d2 0x00c001e2.0119.37 ---- 1 fsc 0x0000.00000000
bdba: 0x0180000f
data_block_dump,data header at 0x7ffff4e4da64
Object id on Block? Y
该块是否属于某个对象
seg/obj: 0x15aaf
0x15aaf 16进制转10进制是88751,也就是t1表的object_id。
itc: 2 数据块内事物槽slot的数量
typ: 1 - DATA 类型1表示数据,类型2表示索引
bdba --Block relative data block address(RDBA)
Xid:事务ID
usn#.slot#.warp#
Undo Segment Number +Transaction Table Slot Number+ Wrap
回滚段号.事务槽号.被重用次数
xid 可以唯一标识一个事务,为什么要记录重用次数?
已提交的事务对应事务槽可以重用,只有回滚段号+事务槽号+重用次数,才能唯一标识一个事务,就像去某餐厅就餐,通过座位号取餐一样,客人离开后座位会被重用给其他客人,但可以通过 餐厅、座位号、座位重用次数唯一标识用餐的客人。
Xid=0x0002.012.000003d2 表示:
2号回滚段,18号事务槽(012的16进制转换为10进制是18),重用了978次(3d2的16进制转换为10进制是978)。
Uba:回滚段地址
该事务对应的回滚段地址
第1段地址:回滚数据块的地址,包括UNDO文件号和数据块号
第2段地址:回滚序列号
第3段地址:回滚记录号
其中:0x00c001e2.0119.37 表示
3号文件(fild_id=3 undotbs01.dbf),482号block(1e2转10进制是482),回滚序列号281,回滚记录号55。
回滚块地址(undo文件号和数据块号)+回滚序列号+回滚记录号
SQL> select ubafil,ubablk,xidusn,xidslot,xidsqn,start_scnb from v$transaction;
UBAFIL UBABLK XIDUSN XIDSLOT XIDSQN START_SCNB
---------- ---------- ---------- ---------- ---------- ----------
3 482 2 18 978 0
Flag:事务标志位。这个标志位就记录了这个事务的操作,各个标志的含义分别是:
----- = 事务是活动的,或者已提交但没有执行块清除。
C--- = 事务已经提交并且清除了行锁。
-B-- = this undo record contains the undo for this ITL entry
--U- = 事务已经提交(SCN已经是最大值),但是锁定还没有清除(快速清除)。
---T =当块清除的SCN被记录时,该事务仍然是活动的,块上如果有已经提交的事务,那么在clean ount的时候,块会被进行清除,但是这个块里面的事务不会被清除。
Lck:影响的记录数
Scn/Fsc:快速提交(Fast Commit Fsc)的SCN或者Commit SCN
......
block_row_dump:
tab 0, row 0, @0x1f87
tl: 17 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [10] 61 20 20 20 20 20 20 20 20 20
tab 0, row 1, @0x1f76
tl: 17 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [10] 62 20 20 20 20 20 20 20 20 20
tab 0, row 2, @0x1f65
tl: 17 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 04
col 1: [10] 78 78 78 78 78 20 20 20 20 20
tab 0, row 3, @0x1f54
tl: 17 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [10] 64 20 20 20 20 20 20 20 20 20
tab 0, row 4, @0x1f43
tl: 17 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [10] 65 20 20 20 20 20 20 20 20 20
end_of_block_dump
其中
tab 0, row 2, @0x1f65
tl: 17 fb: --H-FL-- lb: 0x2 cc: 2
(1) lb: 0x2说明事物在该数据行上的锁还没清除,并且该锁指向02号事物槽。
(2) lb: 和上面的 ITL 的 lck 相对应表示这行是否被lock 了;
(3) cc: 有几列数据 这里只能表示255列 超过了就会有链接行;
可以看到对于的NAME列值,即使事物没有提交,数据块里的第3行NAME值从之前的c变成了xxxxx。
COL 第1行 FOR A5
COL 第2行 FOR A5
COL 第3行 FOR A5
COL 第4行 FOR A5
COL 第5行 FOR A5
SELECT
utl_raw.cast_to_varchar2(replace('61 20 20 20 20 20 20 20 20 20',' ')) AS "第1行",
utl_raw.cast_to_varchar2(replace('62 20 20 20 20 20 20 20 20 20',' ')) AS "第2行",
utl_raw.cast_to_varchar2(replace('78 78 78 78 78 20 20 20 20 20',' ')) AS "第3行",
utl_raw.cast_to_varchar2(replace('64 20 20 20 20 20 20 20 20 20',' ')) AS "第4行",
utl_raw.cast_to_varchar2(replace('65 20 20 20 20 20 20 20 20 20',' ')) AS "第5行"
FROM
dual;
第1行 第2行 第3行 第4行 第5行
----- ----- ----- ----- -----
a b xxxxx d e
和修改数据的会话,看到的数据一致:
SQL> select name from t1;
NAME
----------
a
b
xxxxx
d
e
总结:
执行update cjc.t1 set name=‘xxxxx’ where id=3;后,发生了什么?
1.UNDO段头块更新事物表
事物开始,在UNDO表空间中找到一个相对空闲的UNDO段,在UNDO段头块事物表里找到一行(slot),记录自己的事物信息(xid);
事物表如下,记录了为该事物分配的rollname是_SYSSMU2_2996391332$,dba=0x00c001e2记录了undo block 482,state=9说明是活动的事物。
********************************************************************************
Undo Segment: _SYSSMU2_2996391332$ (2)
********************************************************************************
......
TRN TBL::
index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x00 9 0x00 0x03d3 0x0017 0x0000.0012ef89 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 1721463352
......
0x11 9 0x00 0x03d1 0x0009 0x0000.0012e87f 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721451926
0x12 10 0x80 0x03d2 0x0003 0x0000.00000000 0x00c001e2 0x0000.000.00000000 0x00000001 0x00000000 0
0x13 9 0x00 0x03d1 0x000a 0x0000.0012e8f7 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721452207
0x14 9 0x00 0x03d2 0x0011 0x0000.0012e875 0x00c001e0 0x0000.000.00000000 0x00000001 0x00000000 1721451926
0x15 9 0x00 0x03d2 0x0016 0x0000.0012ec57 0x00c001e1 0x0000.000.00000000 0x00000001 0x00000000 1721454422
2.回滚段中为事务分配UNDO块
将分配的UNDO块地址写入到事务表里,就是第1步的dba地址(dba=0x00c001e2记录了undo block 482);
3.修改数据块事物槽
数据块头部事务槽里,写入xid(指向事务表),uba地址(指定回滚块) 。数据块内的事务槽信息:
Block header dump: 0x0180000f
Object id on Block? Y
seg/obj: 0x15aaf csc: 0x00.12f105 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1800008 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0002.00e.000003d3 0x00c001e2.0119.34 C--- 0 scn 0x0000.0012f05a
0x02 0x0002.012.000003d2 0x00c001e2.0119.37 ---- 1 fsc 0x0000.00000000
bdba: 0x0180000f
data_block_dump,data header at 0x7ffff4e4da64
其中:Xid=0x0002.012.000003d2 表示:
2号回滚段,18号事务槽(012的16进制转换为10进制是18)(也就是undo段头块里事物表的第19行[0-18]),重用了978次(3d2的16进制转换为10进制是978)。
其中:Uba=0x00c001e2.0119.37 表示
3号文件(fild_id=3 undotbs01.dbf),482号block(1e2转10进制是482),回滚序列号281,回滚记录号55。
数据库头部事物槽信息记录了该事物对应undo段头块事物表位置,对应undo块号、位置等信息。
4.undo块记录前镜像
修改前的数据写入到UNDO块,地址指向undo块
itli: 2 ispac: 0 maxfr: 4858
vect = 3
col 1: [10] 63 20 20 20 20 20 20 20 20 20
可以看到修改第3行第二列的前镜像值是:63 20 20 20 20 20 20 20 20 20
转换回原值,是NAME=‘c’
SQL> SELECT utl_raw.cast_to_varchar2(replace('63 20 20 20 20 20 20 20 20 20',' ')) value FROM dual;
VALUE
--------------------------------------------------------------------------------
c
5.修改数据块
tl: 17 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 04
col 1: [10] 78 78 78 78 78 20 20 20 20 20
转换修改后的值,是NAME=‘c’
SQL> SELECT utl_raw.cast_to_varchar2(replace('78 78 78 78 78 20 20 20 20 20',' ')) value FROM dual;
VALUE
--------------------------------------------------------------------------------
xxxxx
可以看到,一个事物最少要修改3种类型块:
1:回滚段头块,更新事物表;
2.数据块,更新头部事物槽、行标志(记录具体slot),更新具体数据;
3.回滚块,数据修改前的值;
并且每次数据块的修改前,都会记录redo log数据。
思考1:事物回滚是怎么实现的?
同一个事物可能对应多个undo块,后一个undo块存有指向前一个undo块的指针,undo块之间组成了一个单向链表,事物槽里记录的是最新undo块的地址。
事物回滚时,通过事物表里找到最新的undo块,根据undo块的数据更新对应数据块的数据进行回滚,然后通过指针找到上一个undo块地址,然后回滚undo块内容,最终依次回滚这个事物对应的所有回滚块,从而完成事物的回滚。
思考2:行锁是怎么实现的?
Oracle的锁机制是一种轻量级的锁定机制,不是通过构建锁列表来进行数据的锁定管理,而是直接将锁作为数据块的属性,存储在数据块首部。
通过ITL来实现的,一个事务要修改块中的数据,必须获得该块中的一个itl。
通过itl和undo segment header中的transaction table,可以知道事务处于活动阶段,还是已经完成。
事务在修改块时(其实就是在修改行)会检查行中row header中的标志位,如果该标志位为0(该行没有被活动的事务锁住,这是可能要进行deferred block cleanout等工作),就把该标志位修改为事务在该块获得的itl的序号,这样当前事务就获得了对记录的锁定,然后就可以修改行数据了,这也就是oracle行锁实现的原理。
如果行未被锁定,会为该事务分配一个ITL slot,并将该ITL的序号写入到row header的lock byte中,从而实现对行的锁定。
此时,其他事务如果试图修改同一行数据,会发现lock byte不为0,从而进入等待状态,直到当前事务提交或回滚并释放锁。
思考3:事物提交后,数据块的事物槽、行锁等信息是立即清除吗?
transaction 提交后, redo完成记录,同时还清除回滚段的事务信息,包括行级锁、ITL 信息 (commit 标志,SCN等) 等。
清除这些事务段的信息的过程就叫做块清除,在完成块清除时, 我们本事务修改的数据块就会存在两种可能:
(1)所有的数据块还保存在buffer cache中;
(2)部分数据块或者是全部数据块由于 LRU 管理 已经被刷出了 buffer cache,存在磁盘上。
oracle 为了考虑到块清除的成本以及性能,会作以下两种方式的块清除处理:
(1)快速块清除 (fast block cleanout )
当事务修改的数据块全部保存在 buffer cache 并且修改数据块的数据量没有超过buffer cache的 10% ,快速清除事务信息。
(2)延迟块清除 (delayed block cleanout)
当修改的数据块的阀值超过10% 或者本次事务相关的数据块已经被刷出了buffer cache, oracle 会下次访问此 block 时再清除事务信息。
这也是为什么有时select语句也会参数少量的redo数据的原因。
思考4:undo段头块和undo普通块损坏数据库会影响数据库启动吗?
undo段头坏损坏可能会自动停库,并且数据库无法open,undo普通块损坏通常不会自动停库,不会影响open,详细内容可以参考我的另一篇文章。
《Oracle 数据库通过BBED模拟UNDO坏块》
本文参考:
相老师的课程:
1.图解深入剖析一个事物的操作流程
2.深入剖析事物槽及Oracle多种提交方式
Oracle数据库UNDO段头块格式深度解析:
https://blog.csdn.net/qq_28721869/article/details/118440656
oracle 块延迟清除(delayed block cleanout)
https://blog.csdn.net/sstevencao/article/details/84124598
###chenjuchao 20240721###
欢迎关注我的公众号《IT小Chen》