首页 > 数据库 > Oracle > 从ITL到Undo前镜像提取实验
“多版本一致读”是Oracle数据库的一个重要特性。Oracle中,一个数据行,如果正在进行数据块修改操作,而且尚未提交。其他会话的用户select的数据是该事务修改之前的数据行数据,也就是其“前镜像”。这样,Oracle中的Select操作是不会被DML事务所阻塞。
读取数据块的前镜像,是基于Oracle Undo机制完成的。当一个数据块要发生事务修改时,会将当前的数据信息复制到Undo Segment的相应位置中。并且作为发生的事务transaction,也会关联上对应Undo Segment中特定的位置信息。
上面的过程有一个重要细节:Undo Segment的前镜像写入操作,同样是有Redo Log Entry生成,记录入Redo Log的。只有这样,才能保证在进行Instance Recovery的过程中,Oracle进行事务前滚、后滚操作时,可以完全Replay Transaction。
当Select操作要检索一个正在进行事务的数据行时,Oracle会根据该行上对应的事务对应的Undo Block Address(UBA)定位到该数据行的前镜像信息。再到Undo Tablespace中找前镜像数据。
本文计划从数据块ITL(Interest Transaction List)事务槽入手,层层定位,演示找到事务数据行对应前镜像的过程。
1、环境准备
我们选择10gR2数据库,使用的环境和数据表同笔者上文《从Dump数据块看ITL》(http://space.itpub.net/17203031/viewspace-716353)。
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production
此时实验数据和Trace跟踪文件位置如下:
SQL> select * from t;
ID VNAME
---------- ----------
1 id
2 iddf
SQL> select id, dbms_rowid.rowid_relative_fno(rowid) fno, dbms_rowid.rowid_block_number(rowid) bno, dbms_rowid.rowid_row_number(rowid) rowno from t;
ID FNO BNO ROWNO
---------- ---------- ---------- ----------
1 1 65266 0
2 1 65266 1
SQL> select f_get_trace_name from dual;
F_GET_TRACE_NAME
--------------------------------------------------------------------------------
C:\TOOL\ORACLE\ORACLE\PRODUCT\10.2.0\ADMIN\OTS\UDUMP\ots_ora_4524.trc
我们Dump出初始的事务ITL列表,如下:
--此时ITL情况
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0007.02a.000085d4 0x00800ca8.35fc.09 C--- 0 scn 0x078c.7f2ddad3
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
注意:默认情况下,Oracle数据块的ITL事务槽初始数目是2。但是在上篇的实验中,我们实验过三个会话事务,用于测试ITL自动延展。现在发现,拓展的ITL列表对象并没有收缩。这说明:ITL一旦拓展,是不会回收缩减的。
2、事务信息定位解析
首先,我们开启一个会话进行实验。
SQL> update t set vname='d' where id=1;
1 row updated
SQL> select addr, xidusn, xidslot, xidsqn, ubafil, ubablk, ubasqn, ubarec, xid from v$transaction;
ADDR XIDUSN XIDSLOT XIDSQN UBAFIL UBABLK UBASQN UBAREC XID
-------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------
27F8EDCC 6 9 34214 2 1762 13420 40 06000900A6850000
注意,在v$transcation中,我们是可以方便的定位到事务标记信息和对应的Undo Segment Block信息的。一个Oracle事务,在定位上是通过usn, slot和sqn唯一确定事务。注意上面代码中的三个标记,我们可以从Dump出的ITL中定位到这些信息。
使用dump命令将对应数据块的信息dump出。
--将数据块DUMP出
SQL> alter system dump datafile 1 block 65266;
System altered
--ITL片段
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0007.02a.000085d4 0x00800ca8.35fc.09 C--- 0 scn 0x078c.7f2ddad3
0x02 0x0006.009.000085a6 0x008006e2.346c.28 ---- 1 fsc 0x0000.00000000
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
注意ITL02的槽位,从LCK信息上看,正在进行一个事务。对应的xid为“0x0006.009.000085a6”。这三个十六进制组成的代码,正好标注出事务的ID编号。
我们可以使用to_number函数将三个片段数据进行十六进制解析。
SQL> select to_number('0006','xxxxx') from dual;
TO_NUMBER('0006','XXXXX')
-------------------------
6 -–与v$transaction中的xidusn对应
SQL> select to_number('009','xxxxx') from dual;
TO_NUMBER('009','XXXXX')
------------------------
9 -–与v$transaction中的xidslot对应
SQL> select to_number('85a6','xxxxx') from dual;
TO_NUMBER('85A6','XXXXX')
-------------------------
34214 -–与v$transaction中的xidsqn对应
说明ITL02的确是对应我们会话事务。
从事务槽中,我们看到了UBA(Undo Block Address)信息,为“0x008006e2.346c.28”。UBA的格式解析为:dba+seq#+rec#。其中的dba为对应的数据文件和数据块的地址。
下面我们需要做的,就是定位到Undo片段的位置。
3、Undo位置确定和前镜像导出
我们需要从UBA地址找到前镜像保存的物理位置,也就是Undo块上的特定片段。首先从DBA中分析出地址。
--将16进制DBA翻译为十进制格式;
SQL> select to_number('8006e2','xxxxxxxx') from dual;
TO_NUMBER('8006E2','XXXXXXXX')
------------------------------
8390370
--使用dbms_utility方法实现转换;
SQL> select dbms_utility.data_block_address_file(8390370) file_no, dbms_utility.data_block_address_block(8390370) block_no from dual;
FILE_NO BLOCK_NO
---------- ----------
2 1762
定位到Undo Block的编号是在fno=2,blockno=1762的位置上。下面需要将其dump出。
SQL> alter system dump datafile 2 block 1762;
System altered
导出的Undo Block格式和一般数据块的Dump结果格式完全不相同。需要说明,一个Undo Block内部的内容可能很多,但是针对需要的前镜像信息,需要通过UBA中的seq#和rec#来确定。
我们首先可以从dump文件中,根据xid编号,找到事务片段。
**************************************************************
UNDO BLK:
xid: 0x0006.009.000085a6 seq: 0x346c cnt: 0x28 irb: 0x28 icl: 0x0 flg: 0x0000
cnt对应的就是UBA中的rec#,而seq对应UBA中的Seq。找到片段如下:
*-----------------------------
* Rec #0x28 slt: 0x09 objn: 112888(0x0001b8f8) objd: 112888 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: 0x00000000
*-----------------------------
uba: 0x008006e2.346c.27 ctl max scn: 0x078c.7f2eae5b prv tx scn: 0x078c.7f2eae65
txn start scn: scn: 0x078c.7f2eb238 logon user: 0
prev brb: 8390369 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x03 ver: 0x01
op: Z
KDO Op code: URP row dependencies Disabled
xtype: XA flags: 0x00000000 bdba: 0x0040fef2 hdba: 0x0040fef1
itli: 2 ispac: 0 maxfr: 4863
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 10
ncol: 2 nnew: 1 size: 1
col 1: [ 2] 69 64
对应的col,就是前镜像数据的体现。我们的update语句修改的是vname列,与col 1相对应。
我们可以将”69 64”翻译为可读格式,判断我们的想法。
set serveroutput on;
declare
n varchar2(10);
begin
dbms_stats.convert_raw_value('6964',n);
dbms_output.put_line(n);
end;
/
SQL>
id
PL/SQL procedure successfully completed
前镜像取值为id,与预想相同。
4、结论
本文简单介绍了如何从一个事务入手,找到前镜像数据的基本方法。我们文中是从itl入手进行研究的,还有可以从v$transaction视图中进行查询UNDO地址信息,效果相同。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31448824/viewspace-2139354/ ,如需转载,请注明出处,否则将追究法律责任。