• Oracle Block浅析2:ITL(Interested Transaction List)


    一.ITL(Interested Transaction List):

      ITL(Interested Transaction List)是Oracle数据块内部的一个组成部分,位于数据块头(block header),itl由xid,uba,flag,lck和scn/fsc组成,
    用来记录该块所有发生的事务,一个itl可以看作是一条事务记录

      当发出一条sql语句时,ORACLE会记录下这个时刻(SCN),然后在buffer cache中查找需要的BLOCK,或者从磁盘上读。当别的会话修改了数据,或者正在修改数据,就会在相应的block上记录ITL,此时ORACLE发现ITL中记录的SCN(Scn/Fsc)大于SELECT时刻的SCN,那么ORACLE就会根据ITL中的Uba找到UNDO信息获得该block的前镜像,然后在buffer cache 中构造出CR(consistent read)块,此时ORALCE 也会检查构造出来的BLOCK中ITL记录的SCN(Scn/Fsc),如果SCN(Scn/Fsc)还大于select时刻的SCN,那么一直重复构造前镜像,然后ORACLE找到前镜像BLOCK中的ITL的SCN是否小于select的SCN,同时检查这个事物有没有提交或者回滚,如果没有,那么继续构造前镜像,直到找到需要的BLOCK,如果在构造前镜像的过程中所需的UNDO信息被覆盖了,就会报快照过旧的错误。

    二.ITL Cleanout和Delayed block cleanout.

      在接触ITL Cleanout和Delayed block cleanout之前先了解一个概念--快速提交:在事务提交(commit)前,会在数据块的头部记录下这个Cleanout SCN(Csc)号、Undo Block Address(Uba)和Transaction ID(Xid);并且在在对应Interested Transaction List(Itl)中设置锁标志,记录这个事务在这数据块中产生的锁的数目;同时在对应修改的数据记录上打上行级锁标志,并映射到对应的Itl去。当提交时,并不会一一清除掉所有锁标志,而是给对应的Itl打上相应标志,告诉后面访问该数据块的事务,相应的事务已经提交。这就叫做快速提交(Fast Commit)。

    2.1导出block信息:

     1 SQL> select * from t;
     2 
     3         ID NAME
     4 ---------- ------------------------------
     5          1 oracle
     6          2 oracle
     7          3 noslq
     8 SQL> update t set name='mysql' where id=2;
     9 
    10 1 row updated.
    11         
    12 SQL> select
    13   2     dbms_rowid.rowid_relative_fno(rowid) REL_FNO,
    14   3     dbms_rowid.rowid_block_number(rowid) BLOCK_NO 
    15   4  from t;
    16 
    17    REL_FNO   BLOCK_NO
    18 ---------- ----------
    19          1      61186
    20          1      61186
    21          1      61186
    22 
    23 SQL> alter system dump datafile 1 block 61186;
    24 
    25 System altered.
    26 
    27 SQL> oradebug setmypid;
    28 Statement processed.
    29 SQL> oradebug tracefile_name
    30 /u01/admin/sun/udump/sun_ora_1832.trc
    31 SQL> !vi /u01/admin/sun/udump/sun_ora_1832.trc

    2.2查看trace文件(commit前):

     1 --------------------
     2 
     3  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
     4 0x01   0x0001.026.00000150  0x008003c0.0113.17  ----    1  fsc 0x0000.00000000
     5 0x02   0x0005.013.00000190  0x0080004b.013a.17  ----    1  fsc 0x0001.00000000
     6 --------------------
     7 block_row_dump:
     8 tab 0, row 0, @0x1f6e
     9 tl: 13 fb: --H-FL-- lb: 0x0  cc: 2
    10 col  0: [ 2]  c1 02
    11 col  1: [ 6]  6f 72 61 63 6c 65
    12 tab 0, row 1, @0x1f55
    13 tl: 12 fb: --H-FL-- lb: 0x2  cc: 2     --对应itl 0x02
    14 col  0: [ 2]  c1 03
    15 col  1: [ 5]  6d 79 73 71 6c
    16 tab 0, row 2, @0x1f61
    17 tl: 13 fb: --H-FL-- lb: 0x1  cc: 2
    18 col  0: [ 2]  c1 04
    19 col  1: [ 6]  6f 72 61 63 6c 65
    20 end_of_block_dump

    2.3 查看trace文件(commit后):即产生快速提交

     1 ----------------
     2 Start dump data blocks tsn: 0 file#: 1 minblk 61186 maxblk 61186
     3 buffer tsn: 0 rdba: 0x0040ef02 (1/61186)
     4 scn: 0x0000.000d206f seq: 0x01 flg: 0x02 tail: 0x206f0601
     5 frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
     6 ----------------
     7  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
     8 0x01   0x0001.026.00000150  0x008003c0.0113.17  ----    1  fsc 0x0000.00000000
     9 0x02   0x0005.013.00000190  0x0080004b.013a.17  --U-    1  fsc 0x0001.000d206f
    10 
    11 -----------------
    12 block_row_dump:
    13 tab 0, row 0, @0x1f6e
    14 tl: 13 fb: --H-FL-- lb: 0x0  cc: 2
    15 col  0: [ 2]  c1 02
    16 col  1: [ 6]  6f 72 61 63 6c 65
    17 tab 0, row 1, @0x1f55
    18 tl: 12 fb: --H-FL-- lb: 0x2  cc: 2     --对应itl 0x02
    19 col  0: [ 2]  c1 03
    20 col  1: [ 5]  6d 79 73 71 6c
    21 tab 0, row 2, @0x1f61
    22 tl: 13 fb: --H-FL-- lb: 0x1  cc: 2
    23 col  0: [ 2]  c1 04
    24 col  1: [ 6]  6f 72 61 63 6c 65
    25 end_of_block_dump

      对比两次ITL(快速提交前后)信息发现:ITL为0x02的 Flag由‘----’变成‘--U-',即事物有活动变为已提交(Scn/Fsc也发生了变化) 。但我们可以看到Lck还是1,也就是说行上的锁还未释放。--U-什么时候会变成C---呢?只有当oracle在该块上发生了itl cleanout或者delayed block cleanout时才会将之变成C---。

    什么是itl cleanout:

      一个新的事务过来时,它首先会选择一个itl槽,首先oracle采用C---状态的事务,如果没有C---状态的事务,oracle就会发生一次itl cleanout,检查所有的ITL相关的事务,如果确认事务已经提交了,就将之修改为C---状态了。

    此时再来一个事务后,可以发现其状态变为:

    1  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    2 0x01   0x0001.026.00000150  0x008003c0.0113.17  ----    1  fsc 0x0000.00000000
    3 0x02   0x0005.013.00000190  0x0080004b.013a.17  C---    0  scn 0x0000.000d206f

    delayed block cleant有两种情况下会发生:

      1.如果一个transaction修改的block超过db cache的10%.

      2.当一个事务未提交时,其修改过的block就已经写到硬盘上去了。此时事务提交了,并不会修改数据块上的状态。

    导出block信息:

     1 SQL> select * from t;
     2 
     3         ID NAME
     4 ---------- ------------------------------
     5          1 oracle
     6          2 mysql
     7          3 noslq
     8 
     9 SQL> update t set name='informax' where id=1;
    10 
    11 1 row updated.
    12 
    13 SQL> select   
    14   2     dbms_rowid.rowid_relative_fno(rowid) REL_FNO,
    15   3     dbms_rowid.rowid_block_number(rowid) BLOCK_NO
    16   4  from t;
    17 
    18    REL_FNO   BLOCK_NO
    19 ---------- ----------
    20          1      61186
    21          1      61186
    22          1      61186
    23 
    24 SQL> 
    25 26 --27 28 SQL> alter system flush buffer_cache; --在未提交的情况下,将数据写如此盘 29 30 System altered. 31 32 SQL>

    --查看trace文件(commit前)

    1 Start dump data blocks tsn: 0 file#: 1 minblk 61186 maxblk 61186
    2 buffer tsn: 0 rdba: 0x0040ef02 (1/61186)
    3 scn: 0x0000.000d32c3 seq: 0x01 flg: 0x04 tail: 0x32c30601
    4 frmt: 0x02 chkval: 0x05fb type: 0x06=trans data
    5 
    6  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    7 0x01   0x0001.001.00000153  0x00800164.0118.19  ----    1  fsc 0x0000.00000000
    8 0x02   0x0005.013.00000190  0x0080004b.013a.17  C---    0  scn 0x0000.000d206f

    --提交

    1 SQL>commit;--正常情况下,会修改block里的scn,但在此种情况下oracle并未修改,oracle只更新undo segment header slot。

    --查看trace文件(commit后)

    1 Start dump data blocks tsn: 0 file#: 1 minblk 61186 maxblk 61186
    2 buffer tsn: 0 rdba: 0x0040ef02 (1/61186)
    3 scn: 0x0000.000d32c3 seq: 0x01 flg: 0x04 tail: 0x32c30601
    4 frmt: 0x02 chkval: 0x05fb type: 0x06=trans data
    5 
    6  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    7 0x01   0x0001.001.00000153  0x00800164.0118.19  ----    1  fsc 0x0000.00000000
    8 0x02   0x0005.013.00000190  0x0080004b.013a.17  C---    0  scn 0x0000.000d206f

      在提交之前buffer cache中的脏数据已经被DBwn进程写回,那么Itl中的事务标志就不会被更新,并且数据块的Itl列表也不会记录下事务的Commit SCN 。后面的事务或查询语句访问该数据块时,为了检测是否需要进行一致性读(如果数据块的Itl中记录的提交事务的Commit SCN 大于当前访问该数据块的SCN,则需要进行一致性读),就需要通过Undo Block Address和Transaction ID到回滚段的事务信息表中去检查前面事务的状态和它的Commit SCN,确定是否做一致性读,最后将前面事务在该数据块上的标志做一次Cleanout。

    --查询

    1 SQL> select * from t;
    2 
    3         ID NAME
    4 ---------- ------------------------------
    5          1 informax
    6          2 mysql
    7          3 noslq
    8 SQL>

    --查看trace文件

    1 Start dump data blocks tsn: 0 file#: 1 minblk 61186 maxblk 61186
    2 buffer tsn: 0 rdba: 0x0040ef02 (1/61186)
    3 scn: 0x0000.000d35df seq: 0x01 flg: 0x00 tail: 0x35df0601
    4 frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
    5 
    6  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    7 0x01   0x0001.001.00000153  0x00800164.0118.19  C---    0  scn 0x0000.000d3322
    8 0x02   0x0005.013.00000190  0x0080004b.013a.17  C---    0  scn 0x0000.000d206f

      对比两次ITL 0x01中的Flag,lck,Scn/Fsc的值,即可发现其变化

    三.根据ITL找数据的前镜像   

    --前面提到了ITL 保存了SCN 和Unto的信息。 在这里,我们就测试一下通过ITL 找到对应的Undo 内容

    3.1.1 准备测试数据

     1 --查看t表的记录
     2 SQL> select * from t;
     3 
     4         ID NAME
     5 ---------- ------------------------------
     6          1 oracle
     7          2 oracle
     8          3 noslq
     9 
    10 SQL>
    11  
    12 -- 查看id=1 记录存储的数据块位置
    13 SQL> select
    14   2     dbms_rowid.rowid_relative_fno(rowid) file_id,
    15   3     dbms_rowid.rowid_block_number(rowid) block_id 
    16   4   from t  where id=3;
    17 
    18    FILE_ID   BLOCK_ID
    19 ---------- ----------
    20          1      61186
    21 
    22 SQL> 
    23  
    24 --更新id=1的数据库,不要提交。此时相应的ITL中会产生一个SCN(Scn/Fsc)
    25 SQL> update t set name='oracle' where id=3;
    26 1 row updated.
    27 SQL>
    28  
    29 -- dump 该数据块(新建会话)
    30 SQL> alter system dump datafile 1 block 61186;
    31 
    32 System altered.
    33 
    34 SQL> 
    35  
    36 --使用oradebug 查看当前trace 文件位置
    37 SQL> oradebug setmypid;
    38 Statement processed.
    39 SQL> oradebug tracefile_name
    40 /u01/admin/sun/udump/sun_ora_1764.trc
    41 SQL>
    42  
    43 --在trace 文件里找到我们的ITL:
    44 
    45  Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    46 0x01   0x0001.026.00000150  0x008003c0.0113.17  ----    1  fsc 0x0000.00000000
    47 0x02   0x0007.02c.00000141  0x0080006d.00de.01  C---    0  scn 0x0000.000d1951
    48  
    49 --因为我们执行了一条update 操作,并且没有commit,那么这里的lock 为1,即Lck值为1. 即对应我们ITL 值为0x01 的记录。
    50 
    51 
    52 SQL> select xidusn,xidslot,xidsqn,ubafil,ubablk,ubasqn,ubarec from v$transaction;
    53 
    54     XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK     UBASQN     UBAREC
    55 ---------- ---------- ---------- ---------- ---------- ---------- ----------
    56          1         38        336          2        960        275         23

    3.1.2 验证XID

     1 --ITL中的XID的格式为:  usn#.slot#.wrap# 
     2 --ITL 为0x01中的XID=0x0001.026.00000150
     3 
     4 SQL> select to_number('0001','XXXXX') from dual;
     5 
     6 TO_NUMBER('0001','XXXXX')
     7 -------------------------
     8                         1
     9                         
    10 SQL>  select to_number('026','XXXXX') from dual;
    11 
    12 TO_NUMBER('026','XXXXX')
    13 ------------------------
    14                       38
    15 
    16 SQL> select to_number('00000150','XXXXXXXXX')from dual;
    17 
    18 TO_NUMBER('00000150','XXXXXXXXX')
    19 ---------------------------------
    20                               336
    21 
    22 这个和V$TRANSACTION中记录的XIDUSN,XIDSLOT,XIDSQN相同。

    3.1.3 验证uba

     1 --UBA的格式为:DBA.seq#.rec#
     2 --ITL为0x01记录中的UBA=0x008003c0.0113.17, 其中0x008003c0表示的DBA(Data Block Address)的地址。
     3  
     4 --将DBA(Data Block Address)解析成file_id 和 block_id.
     5 SQL> select
     6   2     dbms_utility.data_block_address_file(to_number('008003c0','xxxxxxxxxxxx')) file_id,
     7   3     dbms_utility.data_block_address_block(to_number('008003c0','xxxxxxxxxxxx')) block_id
     8   4  from dual;
     9 
    10    FILE_ID   BLOCK_ID
    11 ---------- ----------
    12          2        960
    13 
    14 --将seq#转化成 UBASQN        
    15 SQL> select to_number('0113','xxxx') from dual;
    16 
    17 TO_NUMBER('00DE','XXXX')
    18 ------------------------
    19                      275
    20 
    21 --将rec#转化成 UBAREC
    22 SQL> select to_number('17','xxxx') from dual;
    23 
    24 TO_NUMBER('17','XXXX')
    25 ----------------------
    26                     23
    27  
    28 这个和v$transaction相同。

    3.1.5 dump undo回滚段,找到更改的前镜像

     1 -- dump undo block
     2 SQL> alter system dump datafile 2 block 960;
     3 System altered.
     4  
     5 --get trace file
     6 SQL> oradebug setmypid
     7 Statement processed.
     8 SQL> oradebug tracefile_name
     9 /u01/admin/sun/udump/sun_ora_1629.trc
    10 SQL>
    11 在dump 文件中找到xid 为 0x0001.026.00000150 的记录。
    12 --小技巧:
    13 --这个文件很大,查找很麻烦,我们可以使用vi 的查找功能来实现。
    14 --       (1)vi /opt/oracle/diag/rdbms/nwom/nwom/trace/nwom_ora_13900.trc
    15 --       (2)输入:/0x0001.026.00000150
    16 --       (3)如果只有一条记录,一次就能匹配到,如果有多条记录,我们按字母n,查找下一条,知道找到结果位置。 
    17 -- 
    18 
    19 --查找的信息如下:
    20 ********************************************************************************
    21 UNDO BLK:
    22 xid: 0x0001.026.00000150  seq: 0x113 cnt: 0x17  irb: 0x17  icl: 0x0   flg: 0x0000
    23 
    24 --该数据块的uba=0x008003c0.0113.17。
    25 --uba的格式:DBA.seq#.rec#。
    26 --这个的cnt对应uba中的rec#。根据这个cnt序列号,我们查找对应的具体内容:
    27 
    28 *-----------------------------
    29 * Rec #0x17  slt: 0x26  objn: 52737(0x0000ce01)  objd: 52737  tblspc: 0(0x00000000)
    30 *       Layer:  11 (Row)   opc: 1   rci 0x00
    31 Undo type:  Regular undo    Begin trans    Last buffer split:  No
    32 Temp Object:  No
    33 Tablespace Undo:  No
    34 rdba: 0x00000000
    35 *-----------------------------
    36 uba: 0x008003c0.0113.16 ctl max scn: 0x0000.000d12f4 prv tx scn: 0x0000.000d133f
    37 txn start scn: scn: 0x0000.000d189d logon user: 0
    38  prev brb: 8389563 prev bcl: 0
    39 KDO undo record:
    40 KTB Redo
    41 op: 0x04  ver: 0x01
    42 op: L  itl: xid:  0x000a.021.0000013e uba: 0x00800011.00f1.2a
    43                       flg: C---    lkc:  0     scn: 0x0000.000d1240
    44 KDO Op code: URP row dependencies Disabled
    45   xtype: XA flags: 0x00000000  bdba: 0x0040ef02  hdba: 0x0040ef01
    46 itli: 1  ispac: 0  maxfr: 4863
    47 tabn: 0 slot: 2(0x2) flag: 0x2c lock: 0 ckix: 12
    48 ncol: 2 nnew: 1 size: -1
    49 col  1: [ 5]  6e 6f 73 6c 71
    50 
    51 End dump data blocks tsn: 1 file#: 2 minblk 960 maxblk 960
    52 
    53 
    54 SQL> set serveroutput on
    55 SQL> declare n varchar2(20);
    56   2  begin
    57   3     dbms_stats.convert_raw_value('6e6f736c71',n);
    58   4     dbms_output.put_line(n);
    59   5  end;
    60   6  /
    61 noslq
    62 
    63 PL/SQL procedure successfully completed.
    64 
    65 SQL> 
    66 --通过这个测试,清楚了Oracle 通过ITL 找到数据的前镜像了。 
  • 相关阅读:
    C和C++内存模型
    makefile 学习归纳
    为知笔记给你更多
    二级指针探讨
    使用vscode书写博客
    C/C++ 笔试题一
    从一段经典错误代码说起——关于局部变量指针和函数传参的问题分析
    socket编程再分析(-)——基础
    samba服务器配置
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />详解
  • 原文地址:https://www.cnblogs.com/polestar/p/2953716.html
Copyright © 2020-2023  润新知