ITL与事务处理

一、ITL与事务的关系

 

ITLinterested transaction list)事务槽是Oracle数据块内部的一个组成部分,位于数据块头(block header)。ITLxidubaflaglckSCN/fsc组成,用来记录在该数据块上所有发生的事务。一个ITL槽位可以看作是一条事务记录,它是Oracle中事务处理的关键组件,如果事务已经提交,则该ITL槽位就可以被反复使用,如果一直不提交,则该ITL槽位一直被占用,里面记录着事务信息、回滚段入口、事务类型等。事务提交后,ITL槽位中仍保存着该事务提交时的SCN号。

 

ITL最小值为1,由参数initrans控制(由于兼容性的原因,Oracle会在对象的存储块上分配两个ITL,因此inittrans的最小值实际上为2),这也是在建表时如果不指定initrans参数时的默认取值,最大值为255,由参数maxtrans控制,最大值参数在Oracle 10g以后不能被修改。一个ITL占用块46B的空间,当块中还有一定的free space时,Oracle可以使用free space构建ITL供事务使用,如果没有了free space,则块因为不能分配新的ITL就可能发生ITL等待。

 

当用户发出一条SQL语句时,Oracle会记录下这个时刻的SCN,然后在buffer cache中查找需要的block,或者从磁盘上读取,当别的会话修改了数据,或者正在修改数据,就会在相应的block上记录ITL,此时Oracle发现ITL中记录的SCN大于select时刻的SCN,那么Oracle就会根据ITL中记录的uba找到undo信息,获得该block的前镜像,然后在buffer cache中构造CRconsistent read )块,此时Oracle也会检查构造出来的blockITL记录的SCN,如果SCN仍然大于select时刻的SCN,那么将继续重复构造前镜像,直到前镜像blockITL记录的SCN小于select时刻的SCN,同时检查该事务是否提交或回滚,如果没有,还要继续构造前镜像,直到找到需要的block。如果在构造前镜像过程中所需的undo信息被覆盖了,就会报快照过旧的错误。于是Oracle实现了多版本控制,这就是Oracle多版本的本质,这也就是为什么发出一条select语句时总是会看到consistent gets了。

 

二、ITL等待

 

发生ITL等待的场景有以下两种情况:

1、超过了maxtrans配置的最大ITL数;

2initrans配置不足,且没有足够的free space开扩展ITL

 

解决办法:

 

maxtrans不足:高并发引起,同一数据块上的事务量已经超出了允许的ITL数量。因此需要减少事务的并发量,对于长事务,在保证数据完整性的前提下,增加commit的频率,将长事务变为短事务,以减少资源占用。

 

initrans不足:数据块上的ITL数量并没有达到maxtrans的限制,发生这种情况的表通常是被较多的update,造成预留空间pctfree(默认10%)被填满。此时可增加表的initranspctfree来解决,如果该表上事务的并发量高,可优先增加initrans,增大ITL槽位的初始分配量,反之,则优先增加pctfree,提升ITL槽位的扩展能力。

 

注意:如果是通过alter table方式修改了表的这两个参数,那么只会影响新的数据块,而不会改变已有数据的数据块。

 

三、实验验证ITL与事务的关系

 

连接到scott用户

sqlplus scott/tiger

 

创建测试表,pctfree设为0

create table t1(a number, b varchar2(30)) pctfree 0;

begin

    for i in 1 .. 1000 loop

        insert into t1 values (i, 'data');

    end loop;

    commit;

end;

/

 

查看段的区间分配信息

col segment_name for a20

col tablespace_name for a20

select segment_name, segment_type, tablespace_name, extent_id, file_id, block_id, blocks, bytes from dba_extents where owner = 'SCOTT' and segment_name = 'T1';

SEGMENT_NAME         SEGMENT_TYPE       TABLESPACE_NAME       EXTENT_ID    FILE_ID   BLOCK_ID     BLOCKS      BYTES

-------------------- ------------------ -------------------- ---------- ---------- ---------- ---------- ----------

T1                   TABLE              USERS                         0          4        168          8      65536

 

查看块分配信息

select distinct dbms_rowid.rowid_block_number(rowid) from scott.t1;

 

DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

------------------------------------

                                 171

                                 174

 

由此可知,t1表的数据占用了两个数据块,块编号分别为171174t1段的第一个区间的起始编号为168,该区间由8个数据块组成。

 

下面在同一个数据块171上同时执行多个事务,看看到底会发生什么。

 

session1

 

update scott.t1 set b = 'Oracle data' where a <= 10;

 

已更新10行。

 

session2

 

update scott.t1 set b = 'Oracle data' where a > 10 and a <= 20;

 

已更新10行。

 

session3

 

先确定当前会话的sid

select sid from v$mystat where rownum = 1;

 

       SID

----------

       136

 

update scott.t1 set b = 'Oracle data' where a > 20 and a <= 30;

 

操作被hang住,事务处于等待状态。

 

查看会话的等待事件

col event for a30

select sid, event, seconds_in_wait, state from v$session_wait where sid = 136;

 

       SID EVENT                          SECONDS_IN_WAIT STATE

---------- ------------------------------ --------------- -------------------

       136 enq: TX - allocate ITL entry               242 WAITING

 

此时出现了分配ITL条目的等待。因为默认的初始ITL槽位分配为2,而pctfree0,两个事务不提交,block中就没有足够空间分配ITL了,因此出现了会话被hang住一直在等待ITL的分配。前面的会话提交或回滚后,后面的会话才得以执行。

 

四、ITL进一步研究

 

当一个事务完成时,Oracle需要执行块清理(block cleanout),清理掉这些在数据块上的事务数据,清除ITL中的标志位、行中row header中的标志位等。块清理分为两种:fast commit block cleanoutdeferred block cleanout

 

快速提交块清理(fast commit block cleanout):这是Oracle的默认行为。

延迟块清理(deferred block cleanout):事务提交时,Oracle仅简单的更新相关回滚段的头部信息,而把数据块的清理操作留给后来需要读写这个数据块的操作者(之后的事务)。

 

设想一个update大量数据的操作,因为执行时间较长,一部分已修改的块已被缓冲池flush out写至磁盘,当update操作完成执行commit操作时,为进行块清理,需要将那些已经写至磁盘的数据块重新读入,这将消耗大量I/O,并使commit操作十分缓慢。为解决这个问题,Oracle使用了延迟块清理的方案,对待存在以下情况的块,commit操作不做块清理:

1、在更新过程中,被缓冲池flush out写至磁盘的块;

2、当更新操作涉及的块超过了块缓冲区缓存的10%时,超出部分的块。

 

虽然commit放弃对这些块的清理,但仍会修改回滚段的段头,回滚段的段头包括了段中的事务信息,commit操作将本事务转化为非active状态。

当下一次操作如selectupdateinsertdelete访问到这些块时再来完成对块的清理,这称之为延迟块清理。块延迟清除通过事务槽上的回滚段号、槽号等信息访问回滚段头的事务信息,若事务不再活跃或事务过期则完成块清理。块延迟清除的影响在select操作过程中体现的最为明显,这也是select语句产生redo信息的主要原因。

 

继续前面的案例,先执行一个更新启动一个新的事务,然后查询出此更新涉及的数据块,然后dump该数据块的内容,进一步验证ITL的信息。

 

执行更新

update scott.t1 set b = 'Oracle data' where a = 100;

 

确定更新所在的文件号和块号

select dbms_rowid.rowid_relative_fno(rowid) from scott.t1 where a = 100;

 

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)

------------------------------------

                                   4

 

select dbms_rowid.rowid_block_number(rowid) from scott.t1 where a = 100;

 

DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)

------------------------------------

                                 171

 

开启会话跟踪

alter session set sql_trace=true;

oradebug setmypid

oradebug tracefile_name

c:\oracle\diag\rdbms\mes\mes\trace\mes_ora_3852.trc

 

dump数据块

alter system dump datafile 4 block 171;

 

查看跟踪文件c:\oracle\diag\rdbms\mes\mes\trace\mes_ora_3852.trc,可以看到关于ITL的信息:

Block header dump:  0x010000ab

 Object id on Block? Y

 seg/obj: 0x12458  csc: 0x00.1eb08d  itc: 2  flg: E  typ: 1 - DATA

     brn: 0  bdba: 0x10000a8 ver: 0x01 opc: 0

     inc: 0  exflg: 0

 

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc

0x01   0x0007.015.0000040c  0x00c00618.017a.07  C-U-    0  scn 0x0000.001ea944

0x02   0x0006.00e.000004ee  0x00c011d1.014e.1e  ----    1  fsc 0x0002.00000000

 

flag:事务状态标志,占用块中的一个字节,对应于v$transaction视图中的status字段,意义如下:

----transaction is active or committed pending cleanout

c---transaction has been committed and locks cleaned out

--u-transaction committedmaybe long ago),SCN is an upper bound

-b--this undo record contains the undo for this ITL entry

---ttransaction was still active at block cleanout SCN

 

SCN/fsc:该ITL对应的事务提交时的SCN,那么这里所有槽位上最大的一个SCN号就表示这个block最后被更新时的SCN。每一个事务对应一个ITL记录,如果该事务没有涉及延迟块清理,那么显示的是fsc,如果是延迟块清理,那么显示的就是SCN

 

lck:事务锁影响的记录数。

 

对照视图v$transaction,获取此处update操作对应的事务信息

select xidusn, xidslot, xidsqn, ubafil, ubablk, ubasqn, ubarec from v$transaction;

 

    XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK     UBASQN     UBAREC

---------- ---------- ---------- ---------- ---------- ---------- ----------

         6         14       1262          3       4561        334         30

 

xid:其构成是xidusn.xidslot.xidsqn,三部分信息分别表示

xidusnundo segment number 回滚段号

xidslotslot number 事务槽号

xidsqnsequence number 序列号

 

uba:其构成是dba.ubasqn.ubarec,而dba包含了ubafilubablk的信息,分解如下

select dbms_utility.data_block_address_file(to_number('00c011d1', 'xxxxxxxx')) ubafil, dbms_utility.data_block_address_block(to_number('00c011d1', 'xxxxxxxx')) ubablk from dual;

 

    UBAFIL     UBABLK

---------- ----------

         3       4561

 

ubafilundo block addressuba filenum 回滚块地址 - 文件号

ubablkundo block number 回滚块地址 - 块号

ubasqnuba sequence number 回滚块地址 - 序列号

ubarecuba record number 回滚块地址 - 记录号

 

于是根据uba信息,可以从回滚段的数据块中找到该项事务的回滚信息,为此可以dump回滚段的数据块

alter system dump datafile 3 block 4561;

 

查看跟踪文件信息,找到该事务对应的回滚信息

UNDO BLK: 

xid: 0x0006.00e.000004ee  seq: 0x14e cnt: 0x1e  irb: 0x1e  icl: 0x0   flg: 0x0000

 

这里的seq即序列号ubasqncnt即记录号ubarec。由rec #0x1e可以进一步在trace文件中找到update前的回滚信息

 

* Rec #0x1e  slt: 0x0e  objn: 74840(0x00012458)  objd: 74840  tblspc: 4(0x00000004)

*       Layer:  11 (Row)   opc: 1   rci 0x1d  

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: 0

op: C  uba: 0x00c011d1.014e.1c

KDO Op code: ORP row dependencies Disabled

  xtype: XA flags: 0x00000000  bdba: 0x010000ab  hdba: 0x010000aa

itli: 2  ispac: 0  maxfr: 4858

tabn: 0 slot: 99(0x63) size/delt: 11

fb: --H-FL-- lb: 0x2  cc: 2

null: --

col  0: [ 2]  c2 02

col  1: [ 4]  64 61 74 61

 

fb:行标记,H表示head of rowFL分别表示行的first piecelast piece,说明此行涉及导出的数据块,不存在行链接,又由于块中存在行头,说明也存在行迁移。

lbITL事务槽编号

cc:列的数量

 

回滚前的编码是64 61 74 61,转换为原始字符信息

select chr(to_number(64, 'xx')) || chr(to_number(61, 'xx')) || chr(to_number(74, 'xx')) || chr(to_number(61, 'xx')) undo_data from dual;

 

UNDO_DAT

--------

data

 

以上测试可见,Oracle是通过数据块中的ITL信息来找到事务对应的回滚信息,同时实现了事务的读一致性。如果事务已完成,ITL就可以被重用。

 

五、ITL与CR块

 

Oracle的锁管理是一种轻量级的锁定机制,不是通过构建锁列表来进行数据锁定管理的,而是直接将锁作为数据块的属性存储在数据块头部,通过ITL实现。一个事务要修改块中的数据,必须获得改块中的一个ITL(通过initrans预先分配的或者是通过pctfree space后来构建的),通过ITLundo segment header中的transaction table,可以知道事务处于活动阶段还是已经完成。事务在修改块时会检查row header中的标志位,如果该标志位为0(该行没有被活动的事务锁住,这是可能要进行延迟块清除等工作),就把该标志位修改为事务在该块获得的ITL序号,这样当前事务就获得了对记录的锁定,然后就可以修改行数据了。与此同时,在该事务处理过程中,如果有会话查询该数据块中的数据,Oracle就会读取回滚段中的内容来构造保障数据读一致性的CRconsistent read)块。

 

在多用户并发环境下,一个数据块可以有多个CR版本,Oracle会在下列情况下构造数据块的CR版本:

1、如果一个数据块上有锁,而有会话需要读取这个数据块中的内容,Oracle就会构造该数据块的CR版本;

2、是否需要构造CR块,与SCN密切相关。如果一个查询游标对应的SCN小于数据块当前的SCN,此时Oracle需要构造对应查询游标SCNCR块。

 

以下看一下数据块及其不同版本的例子,操作分别在几个不同会话中进行。

 

session1

查出表中数据所在的文件号和块号

select distinct dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block# from scott.emp;

 

     FILE#     BLOCK#

---------- ----------

   4             151

 

由文件号和块号查询缓存中的数据块,此时还没有该数据块信息

select file#, block#, status, dirty, objd, ts# from v$bh where file# = 4 and block# = 151;

 

未选定行

 

对数据做查询操作

select * from scott.emp;

 

缓存中产生了数据块的xcur版本

select file#, block#, status, dirty, objd, ts# from v$bh where file# = 4 and block# = 151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 xcur       N      73196          4

 

刷新缓存

alter system flush buffer_cache;

 

缓存中的数据块没有消失,但状态变为了free版本

select file#, block#, status, dirty, objd, ts# from v$bh where file# = 4 and block# = 151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

 

对数据再次做查询操作

select * from scott.emp;

 

查询缓存块,此时多了一个xcur版本

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

         4        151 xcur       N      73196          4

 

session2

对数据块进行更新操作,但不提交

update scott.emp set sal = 1000 where empno =7369;

 

session1

查询缓存块,dirty列标志为Y,表示为脏数据

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

 

再次对数据做查询操作

select * from scott.emp;

 

查询缓存块,又多了一个cr版本,因为之前session2的更新没有提交,所以session1查询时,通过读取回滚段在buffer cache中构建了CR块。

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

 

session2

提交更新

commit;

 

session1

再次对数据做查询操作

select * from scott.emp;

 

查询缓存块,状态信息不变,因为是否提交并不影响数据块内容

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

 

session2

再次更新数据,但不提交

update scott.emp set sal = 800 where empno =7369;

 

session1

对数据做查询操作

select * from scott.emp;

 

查询缓存块,可以看到又新构建了一个CR版本

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

         4        151 cr         N      73196          4

 

session2

提交更新

commit;

 

session1

对数据做查询操作

select * from scott.emp;

 

查询缓存块,状态不改变

select file#, block#, status, dirty, objd, ts# from v$bh where file#=4 and block#=151;

 

     FILE#     BLOCK# STATUS     D       OBJD        TS#

---------- ---------- ---------- - ---------- ----------

         4        151 free       N      73196          4

         4        151 xcur       Y      73196          4

         4        151 cr         N      73196          4

         4        151 cr         N      73196          4

 

数据块在缓存中的状态及其物理意义如下:

freenot currently in use

xcurexclusive

scurshared current

crconsistent read

readbegin read from disk

mrecin media recovery mode

irecin instance recovery mode

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/28974745/viewspace-2146505/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/28974745/viewspace-2146505/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值