ITPub博客

首页 > 数据库 > Oracle > 从ITL到Undo前镜像提取实验

从ITL到Undo前镜像提取实验

原创 Oracle 作者:Rman2017 时间:2017-05-19 10:19:41 0 删除 编辑

“多版本一致读”是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/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: 从Dump数据块看ITL
请登录后发表评论 登录
全部评论

注册时间:2017-03-30

  • 博文量
    352
  • 访问量
    132807