客户昨日一套核心系统有进程挂住,top等待事件为enq: TX – allocate ITL entry,判断为itl争用的原因,这方面以前做了不少测试并没有整理,这里顺便做个整理作为诊断手段用。该内容主要分为主要的三个部分:
一.itl的解释以及原理
二.数据块上的initrans不能扩展分配slot导致的itl争用测试
三.索引块上递归事务专用的itl slot争用的识别判断
一.ITL原理解释
ITL(Interested Transaction List)是Oracle数据块内部的一个组成部分,用来记录该块所有发生的事务,一个itl可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个itl的位置就可以被反复使用了,因为itl类似记录,所以,有的时候也叫itl槽位。Oracle的每个数据块中都有一个或者多个事务槽,每一个对数据块的并发访问事务都会占用一个事务槽。 表和索引的事务槽ini_trans是1、max_trans是255,在oracle10g中,不能修改max_trans这个参数,因为oracle10g忽略了这个参数。如果一个事务一直没有提交,那么,这个事务将一直占用一个itl槽位,itl里面记录了事务信息,回滚段的嵌入口,事务类型等等。如果这个事务已经提交,那么,itl槽位中还保存的有这个事务提交时候的SCN号。如dump一个块,就可以看到itl信息:
Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0006.002.0000158e 0x0080104d.00a1.6e --U- 734 fsc 0x0000.6c9deff0 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
如果在并发量特别大的系统中,最好分配足够的itl个数(10g之前的版本),其实它并浪费不了太多的空间,或者,设置足够的pctfree,保证itl能扩展,但是pctfree有可能是被行数据给消耗掉的,如update可能一下占满块空间,所以,也有可能导致块内部的空间不够而导致itl等待,所以在通常情况下,10g版本后引起itl等待的原因往往是因为块的空间不足导致,并不是tran事务槽数量不足,在正常情况下2k的数据块最多可以拥有41个itl,4k数据块最多拥有83,8k最多用友169个itl(以itl 24byte为单位)。INITRANS不足的问题不会出现在索引数据块上,当发现没有足够空间分配ITL slot时,无论是枝点块还是叶子块,数据块会发生分裂(Index Block Split)。
有一种特殊情况也会引起ITL的等待,就是在索引上的递归事务itl争用,这种情况比较特殊。在索引的枝节点上,有且只有一个ITL slot,它是用于当发生节点分裂的递归事务(Recursive Transaction)。在叶子节点上,第一条ITL Slot也是用于分裂的递归事务的。在一个用户事务中,如果发生多次分裂,每一次分裂都是由一个单独的递归事务控制的,如果下层节点分裂导致其父节点分裂,它们的分裂则由同一个递归事务控制。当2个事务同时需要分裂一个枝节点或者叶子节点时,或者枝节点下的2个子节点分别被2个事务分裂,就会造成这种ITL等待。
2.数据块上的initrans不能扩展分配slot导致的itl争用测试
我们做个测试关于如果数据块没有空间留给itl slot扩展时候的测试,创建表luda,指定pctfree为0,同时指定initrans为1然后填满相关数据块,再对块满的数据进行更新模拟出itl的等待。
创建表luda,并指定pctfree为0,initrans为1
SQL> create table luda(a int) pctfree 0 initrans 1; Table created.
插入大量数据
SQL> begin 2 for i in 1..20000 loop 3 insert into luda values(i); 4 end loop; 5 end; 6 / PL/SQL procedure successfully completed. SQL> commit ; Commit complete. SQL> select f,b,count(*) from (select dbms_rowid.rowid_relative_fno(rowid) f,dbms_rowid.rowid_block_number(rowid) b 2 from luda) group by f,b order by 3; F B COUNT(*) ---------- ---------- ---------- 1 61101 200 1 61093 734 1 61089 734 1 61095 734 1 61085 734 1 61099 734 1 61074 734 1 61077 734 1 61080 734 1 61092 734 1 61100 734 1 61083 734 1 61091 734 1 61097 734 1 61098 734 1 61075 734 1 61076 734 1 61078 734 1 61081 734 1 61084 734 1 61087 734 1 61096 734 1 61079 734 1 61094 734 1 61088 734 1 61090 734 1 61082 734 1 61086 734
插入20018条数据后可以发现该表有26个数据块,填满了除了61101块意外的其他数据块。
接着导出已经填满的数据块61074.
SQL> alter system dump datafile 1 block 61074; System altered. Block header dump: 0x0040ee92 Object id on Block? Y seg/obj: 0xcb0a csc: 0x00.bb6a1 itc: 2 flg: - typ: 1 - DATA fsl: 0 fnx: 0x0 ver: 0x01 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0005.020.0000013b 0x0080078c.013c.3a --U- 734 fsc 0x0000.000bb765 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000 --可以发现initrans为1的情况下默认是有2个事务槽,itc=2 data_block_dump,data header at 0xd7fe45c =============== tsiz: 0x1fa0 hsiz: 0x5ce pbl: 0x0d7fe45c bdba: 0x0040ee92 76543210 flag=-------- ntab=1 nrow=734 frre=-1 fsbo=0x5ce fseo=0xbf8 avsp=0x4 tosp=0x4 0xe:pti[0] nrow=734 offs=0
块满的情况测试slot的分配,根据前面的查询结果我们知道单个块的存储行数为734行,也可以通过dump中的nrow=734得知,所以我们在这部测试中依次更新第100,200,300行的数据。
session 1 更新第100行的数据:
SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61074 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=100; 1 row updated.
session 2更新第200行的数据:
SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61074 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=200; 1 row updated.
session 3更新第300行的数据,session3的sid为158,并且在执行过程中session 3 hang住:
SQL> select sid from v$mystat where rownum=1; SID ---------- 158 SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61074 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=300; --此时进程hang住 alter system dump datafile 1 block 61074; Block header dump: 0x0040ee92 Object id on Block? Y seg/obj: 0xcb0a csc: 0x00.bb97e itc: 2 flg: - typ: 1 - DATA fsl: 0 fnx: 0x0 ver: 0x01 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0003.014.00000159 0x008006bb.01f1.12 ---- 1 fsc 0x0000.00000000 0x02 0x0005.00e.0000013c 0x0080083c.014b.20 ---- 1 fsc 0x0000.00000000 --通过此时的dump我们也可以发现原先为被占用的2个事务槽已经被占用而且事务未提交。 data_block_dump,data header at 0xd77645c =============== tsiz: 0x1fa0 hsiz: 0x5ce pbl: 0x0d77645c bdba: 0x0040ee92 76543210 flag=-------- ntab=1 nrow=734 frre=-1 fsbo=0x5ce fseo=0xbf8 avsp=0x4 tosp=0x4 0xe:pti[0] nrow=734 offs=0 --查询158进程的等待事件为itl的相关等待事件enq: TX - allocate ITL entry SQL> select sid,event from v$session where sid=158; SID EVENT ---------- ---------------------------------------------------------------- 158 enq: TX - allocate ITL entry
从以上验证了空间不足的情况下会导致itl无法分配引起enq: TX – allocate ITL entry等待事件的产生。
接下来测试块不满的情况,在表luda中目前有数据的块为26个,其中块号为61101的块只有200条数据,只占用该块30%的空间,为了测试需要对61101号块采用4个session分别对第10,20,30,40行进行更新:
session 1:
SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61101 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=10; 1 row updated.
session2:
SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61101 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=20; 1 row updated.
session3:
SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61101 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=30; 1 row updated.
session4:
SQL> update luda set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=61101 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=40; 1 row updated.
以上4个session都没有遇到阻塞,导出61101号块可以发现该块有4个itl slot,自动扩展了2个slot,验证了在空间足够的情况下itl slot会自动扩展。
SQL> alter system dump datafile 1 block 61101; System altered. -- Block header dump: 0x0040eead Object id on Block? Y seg/obj: 0xcb0a csc: 0x00.bc003 itc: 4 flg: O typ: 1 - DATA fsl: 0 fnx: 0x0 ver: 0x01 Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0003.014.00000159 0x008006bb.01f1.13 ---- 1 fsc 0x0000.00000000 0x02 0x0009.016.0000013b 0x0080075c.0143.37 ---- 1 fsc 0x0000.00000000 0x03 0x0005.00e.0000013c 0x0080083c.014b.21 ---- 1 fsc 0x0000.00000000 0x04 0x0002.026.00000139 0x00800130.00fc.09 ---- 1 fsc 0x0000.00000000 --itc=4,在61101块上存在4条未提交的事务,分别是我们刚才执行的sesson1-4. data_block_dump,data header at 0xe17048c =============== tsiz: 0x1f70 hsiz: 0x1a2 pbl: 0x0e17048c bdba: 0x0040eead 76543210 flag=-------- ntab=1 nrow=200 frre=-1 fsbo=0x1a2 fseo=0x1957 avsp=0x16c6 tosp=0x16c6 0xe:pti[0] nrow=200 offs=0
以上测试说明了在Oracle10g以来,itl的争用事件产生在数据块上主要是受块空间的影响,如果大量事务在相关block上执行操作但是当块无法满足分配事务槽,则会产生itl相关争用。
下一节主要是区分是索引块还是表数据块的争用. http://www.ludatou.com/?p=1920