ITL争用

 

Waits for TX in mode 4 also occur when a transaction inserting a row in an index has to wait for the end of an index block split being done by another transaction. This type of TX enqueue wait corresponds to the wait event enq: TX - index contention.

可以认为一个session在向一个索引块中执行插入时产生了索引块的split,而其它的session也要往该索引块中插入数据,此时,其它session必须要等待split完成,由此引发了该等待事件。

从抓取的ash报告来看,产生等待的是一条insert语句,而该sql要插入数据的表是一个每天需要进行频繁delete的表,该等待事件的产生与频繁的大批量delete是具有紧密联系的。厂商最后给出的建议是定期对该表进行rebuild,并加大索引的pctfree

二、

事务队列等待(Tx Enqueue)深入分析——ITL争用

作者: fuyuncat

来源: www.HelloDBA.com

    我们知道,事务在对数据块中的记录加锁时,需要首先在数据块头部记录下该事务的相关信息,这样一个记录就是一条ITL槽(slot)。ITL TX等待发生在事务请求对数据块中记录加锁时,数据块上没有足够ITL槽。

    导致发生ITL不足的原因有3种:

  • INITRANS太小,没有保留足够的ITL空间,当数据块被数据记录占满(或接近满)后,数据块上没有足够空间创建新的ITL槽位(尽管数据块上ITL数量没有达到MAXTRANS的限制),这时,新的事务向其申请加锁时,就会发生ITL争用等待。

    注:尽管有PCTFREE为数据块预留了空间,但是,UPDATE操作可能会将其占用,导致空间不足。
   
2INITRANS不足的问题不会出现在索引数据块上,当发现没有足够空间分配ITL slot时,无论是枝点块还是叶子块,数据块会发生分裂(Index Block Split)。

  • MAXTRANS不足。当受到MAXTRANS限制时,也会发生此类等待。MAXTRANS的默认值是255(10g以后则不能修改这一参数),但是其实际大小是受到块的大小的限制的。ITL Slot占用的空间不会超过块大小的50%(实际上,如数据块为2K,则ITL最多为414k数据块的ITL最大数为83,8K数据块则为169)。
  • 索引块上的递归事务的ITL slot争用。这一类等待比较特殊。在索引的枝节点上,有且只有一个ITL slot,它是用于当发生节点分裂的递归事务(Recursive Transaction)。在叶子节点上,第一条ITL Slot也是用于分裂的递归事务的。在一个用户事务中,如果发生多次分裂,每一次分裂都是由一个单独的递归事务控制的,如果下层节点分裂导致其父节点分裂,它们的分裂则由同一个递归事务控制。当2个事务同时需要分裂一个枝节点或者叶子节点时,或者枝节点下的2个子节点分别被2个事务分裂,就会造成这种ITL等待。

    下面的代码模拟第一种情形导致的TX锁(继续使用之前的表进行演示)——表创建时,INITRANS默认值为1

SQL代码

1.    -- 创建索引,增加分析干扰   

2.    HELLODBA.COM> create index tx_lock_tab_idx on tx_lock_tab (c);   

3.      

4.    index created.   

5.      

6.    HELLODBA.COM> begin  

7.      2    for i in 1..5000 loop   

8.      3      insert into tx_lock_tab (a, b, c) values(i, 'E', lpad('A', 8, 'A'));   

9.      4    end loop;   

10.    5  end;   

11.    6  /   

12.    

13.  PL/SQL procedure successfully completed.   

14.    

15.  --将一个数据块的记录大小增大,填充PTCFREE留下的空闲空间   

16.  HELLODBA.COM> update tx_lock_tab set c=lpad('A', 10, 'A'where a between 3633 and 3995;   

17.    

18.  363 rows updated.   

19.    

20.  HELLODBA.COM> update tx_lock_tab set c=lpad('A', 60, 'A'where a=3633;   

21.    

22.  1 row updated.   

23.    

24.  HELLODBA.COM> commit;   

25.    

26.  Commit complete.  

    通过dump出数据块,可以看到数据块上空闲空间已经极少了:

SQL代码

1.    ...   

2.    fsbo=0x2e8   

3.    fseo=0x30d   

4.    0x30d - 0x2e8 = 25d   

5.    ...  

    仅够容纳1ITL了(24字节),加上数据块上原有了2ITL slot,这个数据块上只能容纳最多3个事务:

SQL代码

1.    --Session 1:   

2.    HELLODBA.COM> update tx_lock_tab set c=lpad('1',10,'1'where a=3635;   

3.      

4.    1 row updated.   

5.      

6.    -- 注意:这条语句与ITL等待无直接关系,只是加入的一个干扰因素   

7.    HELLODBA.COM> update t_test5 set username='AAA' where user_id=1;   

8.      

9.    1 row updated.   

10.    

11.  --Session 2:   

12.  HELLODBA.COM> update tx_lock_tab set c=lpad('1',10,'1'where a=3636;   

13.    

14.  1 row updated.   

15.    

16.  HELLODBA.COM> update t_test5 set username='AAA' where user_id=2;   

17.    

18.  1 row updated.   

19.    

20.  --Session 3:   

21.  HELLODBA.COM> update tx_lock_tab set c=lpad('1',10,'1'where a=3637;   

22.    

23.  1 row updated.   

24.    

25.  HELLODBA.COM> update t_test5 set username='AAA' where user_id=3;   

26.    

27.  1 row updated.   

28.    

29.  --第四个事务被阻塞:   

30.  HELLODBA.COM> update tx_lock_tab set c=lpad('1',10,'1'where a=3638;  

等待队列分析

    当发生ITL等待时,锁的请求模式是共享(4)模式,此时,通过V$ENQUEUE_LOCK就可以观察到TX锁等待:

SQL代码

1.    HELLODBA.COM> select * from V$ENQUEUE_LOCK where type='TX';   

2.      

3.    ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK   

4.    -------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------   

5.    1EEBCDE0 1EEBCDF4        323 TX     589858     142485          0          4       1366          0  

    但是,除了ITL等待之外,还有其它几种等待也是共享模式,因此我们不能仅仅通过其请求模式来判断是ITL等。我们可以通过session event来鉴定这类等待队列,其对应的事件为“enq: TX - allocate ITL entry”

SQL代码

1.    HELLODBA.COM>select s.sid, e.event, s.row_wait_obj#, o.object_name   

2.      2   from v$session s, v$enqueue_lock l, v$session_event e, dba_objects o   

3.      3   where e.sid=l.sid   

4.      4   and e.sid = s.sid   

5.      5   and s.row_wait_obj# = o.object_id(+)   

6.      6   and e.event like 'enq: TX%';   

7.      

8.           SID EVENT                          ROW_WAIT_OBJ#     OBJECT_NAME   

9.    ---------- ------------------------------ ----------------- ------------------   

10.         323 enq: TX - allocate ITL entry   198062            T_TEST5  

    这里有一点要注意,当请求模式为共享模式时,v$sessionROW_WAIT_*字段信息是并不准确,你可以将其作为参考,但是它可能并不是实际请求的对象。以ROW_WAIT_OBJ#为例,它可能为持锁会话中最后一次加锁的对象(如上例,为T_TEST5,并非真正导致等待的对象TX_LOCK_TAB),大多数情况下其值为-10

    和记录锁不同,ITL等待可能发生在表上,也可能发生在索引上。要精确定位导致发生这一等待事件的对象,分析过程就相对复杂一些。

    首先,通过被阻塞事务的请求锁的ID1ID2找到事务的回滚段信息:

SQL代码

1.    HELLODBA.COM> select l.sid req_session, s.sid lock_session, l.lmode, l.request, t.xidusn, t.xidslot, t.start_ubafil, t.start_ubablk, t.start_ubarec   

2.      2  from v$lock l, v$transaction t, v$session s   

3.      3  where l.type = 'TX'  

4.      4  and trunc(id1/power(2,16)) = t.xidusn   

5.      5  and l.id2 = t.xidsqn   

6.      6  and id1 - power(2,16)*trunc(id1/power(2,16)) = t.xidslot   

7.      7  and t.addr = s.taddr   

8.      8  and l.request = 4;   

9.      

10.  REQ_SESSION LOCK_SESSION XIDUSN    XIDSLOT START_UBAFIL START_UBABLK  USED_UBLK START_UBAREC   

11.  ----------- ------------ --------- ------- ------------ ------------- --------- ------------   

12.          323          311        29       7            2          4197         1            1  

    被阻塞事务使用到UNDO数据块为文件24197UNDO开始记录为1,且只用到一个回滚块。我们将该UNDOdump出来:

SQL代码

1.    HELLODBA.COM> alter system dump datafile 2 block 4197;   

2.      

3.    System altered.  

    找到对应的回滚记录。可以看到,从第10x1)条记录开始,slot70x07)的记录有4条:

SQL代码

1.    *-----------------------------   

2.    * Rec #0x1  slt: 0x07  objn: 198074(0x000305ba)  objd: 198074  tblspc: 5(0x00000005)   

3.    *       Layer:  11 (Row)   opc: 1   rci 0x00      

4.    Undo type:  Regular undo    Begin trans    Last buffer split:  No    

5.    Temp Object:  No    

6.    Tablespace Undo:  No    

7.    rdba: 0x00000000   

8.    *-----------------------------   

9.    uba: 0x00801064.0058.01 ctl max scn: 0x0000.b0e03719 prv tx scn: 0x0000.b0e03765   

10.  txn start scn: scn: 0x0000.b0e0e1fa logon user: 35   

11.   prev brb: 8388927 prev bcl: 0   

12.  KDO undo record:   

13.  KTB Redo    

14.  op: 0x04  ver: 0x01     

15.  op: L  itl: xid:  0x0018.012.00000252 uba: 0x00800367.00f2.35   

16.                        flg: C---    lkc:  0     scn: 0x0000.b0db5fed   

17.  KDO Op code: URP row dependencies Disabled   

18.    xtype: XA flags: 0x00000000  bdba: 0x0141072a  hdba: 0x01410723   

19.  itli: 3  ispac: 0  maxfr: 4858   

20.  tabn: 0 slot: 2(0x2) flag: 0x2c lock: 0 ckix: 28   

21.  ncol: 3 nnew: 1 size: 0   

22.  col  2: [10]  31 31 31 31 31 31 31 31 31 31   

23.      

24.  *-----------------------------   

25.  * Rec #0x2  slt: 0x07  objn: 198176(0x00030620)  objd: 198176  tblspc: 5(0x00000005)   

26.  *       Layer:  10 (Index)   opc: 22   rci 0x01      

27.  Undo type:  Regular undo   Last buffer split:  No    

28.  Temp Object:  No    

29.  Tablespace Undo:  No    

30.  rdba: 0x00000000   

31.  *-----------------------------   

32.  index undo for leaf key operations   

33.  KTB Redo    

34.  op: 0x04  ver: 0x01     

35.  op: L  itl: xid:  0x0018.012.00000252 uba: 0x00800367.00f2.37   

36.                        flg: C---    lkc:  0     scn: 0x0000.b0db5fed   

37.  Dump kdilk : itl=3, kdxlkflg=0x1 sdc=0 indexid=0x1415c03 block=0x01415c05   

38.  (kdxlre): restore leaf row (clear leaf delete flags)   

39.  key :(18):  0a 31 31 31 31 31 31 31 31 31 31 06 01 41 07 2a 00 02   

40.      

41.  ...   

42.    

43.  *-----------------------------   

44.  * Rec #0x4  slt: 0x07  objn: 198062(0x000305ae)  objd: 198062  tblspc: 5(0x00000005)   

45.  *       Layer:  11 (Row)   opc: 1   rci 0x0f      

46.  Undo type:  Regular undo   Last buffer split:  No    

47.  Temp Object:  No    

48.  Tablespace Undo:  No    

49.  rdba: 0x00000000   

50.  *-----------------------------   

51.  KDO undo record:   

52.  KTB Redo    

53.  op: 0x04  ver: 0x01     

54.  op: L  itl: xid:  0x0019.02a.0000009a uba: 0x008000eb.0077.02   

55.                        flg: C---    lkc:  0     scn: 0x0000.b0de9878   

56.  KDO Op code: URP row dependencies Disabled   

57.    xtype: XA flags: 0x00000000  bdba: 0x014103d7  hdba: 0x014103d3   

58.  itli: 1  ispac: 0  maxfr: 4858   

59.  tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 28   

60.  ncol: 3 nnew: 1 size: 0   

61.  col  0: [ 3]  41 41 41  

    事务对象分别是T_TEST5(objn: 198062)TX_LOCK_TAB_IDX(objn: 198176)TX_LOCK_TAB(objn: 198074),其中,对于索引TX_LOCK_TAB_IDX来说,一个UPDATE操作实际是是一个INSERT操作加DELETE操作,因此存在2条记录。那么,哪个才是导致ITL等待的对象呢?我们可以先找到被阻塞会话当前正在执行的语句(即被阻塞的语句):

SQL代码

1.    HELLODBA.COM> select s.sid, s.event, s.wait_time, q.sql_text   

2.      2  from v$session s, v$sqlarea q   

3.      3  where s.sql_address = q.address(+)   

4.      4  and s.sql_hash_value = q.hash_value(+)   

5.      5  and s.sid = 323;   

6.      

7.    SQL_TEXT   

8.    --------------------------------------------------------------------------------   

9.    update tx_lock_tab set c=lpad('D',10,'D'where a=3638  

    可以看到,被阻塞事务正在执行对TX_LOCK_TABUPDATE操作,而T_TEST5与其没有任何关联(没主外键关系),因而可以被排除。

    我们先确认是不是索引数据块引起的。从UNDO记录中找到索引数据块地址(block=0x01415c05),dump出来:

SQL代码

1.    HELLODBA.COM> select dbms_utility.data_block_address_file(TO_NUMBER('01415c05''XXXXXXXX')) file_id,   

2.      2         dbms_utility.data_block_address_block(TO_NUMBER('01415c05''XXXXXXXX')) block_id from dual;   

3.      

4.       FILE_ID   BLOCK_ID   

5.    ---------- ----------   

6.             5      89093   

7.      

8.    HELLODBA.COM> alter system dump datafile 5 block 89093;   

9.      

10.  System altered.  

    Trace文件内容:

SQL代码

1.     seg/obj: 0x30620  csc: 0x00.b0e0e1fa  itc: 4  flg: E  typ: 2 - INDEX  

2.         brn: 0  bdba: 0x1415c01 ver: 0x01 opc: 0   

3.         inc: 0  exflg: 0   

4.        

5.     Itl           Xid                  Uba         Flag  Lck        Scn/Fsc   

6.    0x01   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000   

7.    0x02   0x0037.002.0000005c  0x008011c3.0051.0c  ----    1  fsc 0x0016.00000000   

8.    0x03   0x001d.007.00000060  0x00801065.0058.02  ----    1  fsc 0x0016.00000000   

9.    0x04   0x0019.015.000000a5  0x008000ec.007f.02  ----    1  fsc 0x0016.00000000   

10.      

11.  Leaf block dump   

12.  ===============   

13.  ...   

14.  kdxcofbo 762=0x2fa   

15.  kdxcofeo 1306=0x51a  

16.  kdxcoavs 684   

17.  ...  

    可以看到,itc4,且空闲空间(kdxcoavs)为684字节,不会造成ITL不足等待。此外,其第一条ITL Slot(分裂事务使用)没有被占用,说明不存在对该ITL slot的争用。用同样方法,可以确认另外一个索引块没有造成ITL等待。

    然后,再次确认表的数据块。同样还是将其(bdba: 0x0141072adump出来:

SQL代码

1.     seg/obj: 0x305ba  csc: 0x00.b0e0e431  itc: 3  flg: E  typ: 1 - DATA   

2.         brn: 1  bdba: 0x1410721 ver: 0x01 opc: 0   

3.         inc: 0  exflg: 0   

4.        

5.     Itl           Xid                  Uba         Flag  Lck        Scn/Fsc   

6.    0x01   0x0037.002.0000005c  0x008011c3.0051.0b  ----    1  fsc 0x0000.00000000   

7.    0x02   0x0019.015.000000a5  0x008000ec.007f.01  ----    1  fsc 0x0000.00000000   

8.    0x03   0x001d.007.00000060  0x00801065.0058.01  ----    1  fsc 0x0000.00000000   

9.        

10.  data_block_dump,data header at 0xad4587c  

11.  ===============   

12.  ...   

13.  fsbo=0x2e8   

14.  fseo=0x2f5   

15.  avsp=0xd   

16.  ...  

    可以发现itc3,没有达到max trans的限制,但是其空闲空间只有0xd=13(avsp),不足以容纳1ITL slot(24字节)了,因此判定是表TX_LOCK_TAB的数据块上空间不足导致的ITL等待。

    有一点要注意,只有当阻塞事务的ITL或者其ITL表之前事务的释放ITL空间时,被阻塞进程才能继续。例如,如果导致阻塞的事务在数据块中ITL序号为0x02,那么如果没有其它事务等待0x01事务的ITL的话,无论是0x01还是0x02的事务被释放,被阻塞的事务都能分配到释放的ITL空间,从而继续;而如果是0x03的事务被释放,被阻塞事务仍然被阻塞。如上例中,从v$transaction找到XID信息得知,导致阻塞的事务的ITL序号是0x01,而此时如果释放第二个事务。
注意:因为ITL空间分配后不会被回收,当这种情况发生时,并不会影响到之前的分析。
 

     递归事务对ITL slot请求分配是在递归事务内部完成,因此因为递归事务的ITL等待出现的时间周期很短暂,我们通过性能视图观察到的这一类等待基本上是由于INITRANSMAXTRANS引起的。大量ITL等待的出现会影响系统和应用的性能,我们可以通过以下查询观察到在哪个对象上发生ITL等待等待最多,并依此对相应的对象或应用进行调整:

SQL代码

1.    HELLODBA.COM>select *   

2.      2    from (select owner, object_name, object_type, value   

3.      3            from v$segment_statistics   

4.      4           where object_name not like 'BIN%'  

5.      5             and statistic_name = 'ITL waits'  

6.      6           order by value desc)   

7.      7   where rownum <= 10;  

死锁分析

    当死锁事务中存在ITL等待时,我们可以按照以下思路进行分析。之前说过,ITL等待不是一个经常出现的事件,偶尔的导致的死锁问题我们或许可以忽略。但是如果经常出现由此导致的死锁就应该引起注意了。判断死锁等待是否涉及ITL等待,可以通过从Trace文件中找到关联会话的详细Trace部分,可以发现以下等待事件信息:

SQL代码

1.    last wait for 'enq: TX - allocate ITL entry' blocking sess=0x1EDDD0DC seq=74 wait_time=2999976 seconds since wait started=111   

2.                name|mode=54580004, usn<<16 | slot=90022, sequence=22c95   

3.    Dumping Session Wait History   

4.     for 'enq: TX - allocate ITL entry' count=1 wait_time=2999976   

5.                name|mode=54580004, usn<<16 | slot=90022, sequence=22c95  

    然而,和v$sessionROW_WAIT_*字段一样,如果死锁中存在共享锁,那么trace文件中Rows waited on部分的信息可能并不正确。我们需要一个更加详细的deadlock trace来做深入分析。

SQL代码

1.    HELLODBA.COM> alter system set events '60 trace name ERRORSTACK level 3; name systemstate level 266';   

2.      

3.    System altered.  

    以下的代码模拟了IndexMaxtrans限制而导致死锁(为了减少事务量,我将模拟ITL等待的索引建立在块大小为2k的表空间上):

SQL代码

1.    --建立测试表和SP   

2.    HELLODBA.COM> create table tx_test_itl (a number, b varchar2(800), c date) logging pctfree 10;   

3.      

4.    Table created.   

5.      

6.    -- 我们需要在这个索引上造成ITL等待,为了减少事务量,将其建立在数据块为2k的表空间上   

7.    HELLODBA.COM> create index tx_test_itl_idx1 on tx_test_itl (a) tablespace idx_2k pctfree 10;   

8.      

9.    Index created.   

10.    

11.  HELLODBA.COM> create index tx_test_itl_idx2 on tx_test_itl (c) tablespace ringidx pctfree 10;   

12.    

13.  Index created.   

14.    

15.  HELLODBA.COM> begin  

16.    2     for i in 1..5300   

17.    3     loop   

18.    4         insert into tx_test_itl (a, b, c) values (i, dbms_random.string(1,trunc(dbms_random.value()*10)), sysdate);   

19.    5     end loop;   

20.    6     delete from tx_test_itl where (trunc(a/4) = a/4 or trunc(a/9) = a/9);   

21.    7  end;   

22.    8  /   

23.    

24.  PL/SQL procedure successfully completed.   

25.    

26.  HELLODBA.COM> commit;   

27.    

28.  Commit complete.   

29.    

30.  HELLODBA.COM> select count(*) from tx_test_itl;   

31.    

32.    COUNT(*)   

33.  ----------   

34.        3534   

35.    

36.  HELLODBA.COM> create or replace procedure recruit_insert( p_cnt in number, p_str in varchar2, p_max in number)   

37.    2  as  

38.    3     pragma autonomous_transaction;   

39.    4  begin  

40.    5     if (p_cnt > p_max)   

41.    6     then  

42.    7             return;   

43.    8     end if;   

44.    9     update t_test1 set created=sysdate, subobject_name='AAA' where object_id = trunc(dbms_random.value()*10000);   

45.   10     --update t_test1 set subobject_name='AAA' where object_id = p_cnt;   

46.   11     insert into tx_test_itl values (p_cnt, p_str, sysdate);   

47.   12     recruit_insert(p_cnt+1, p_str, p_max);   

48.   13     if (p_cnt = p_max) then  

49.   14             sys.dbms_lock.sleep(60);   

50.   15     end if;   

51.   16     update t_test5 set username='AAA' where user_id=1;   

52.   17     rollback;   

53.   18  end;   

54.   19  /   

55.    

56.  Procedure created.   

57.    

58.  --会话1中执行   

59.  HELLODBA.COM> update t_test5 set username='AAA' where user_id=1;   

60.    

61.  1 row updated.   

62.    

63.  --会话2中执行。为了达到所有40个事务都在同一个数据块上的效果,tx_test_itl_idx1的最后一个数据块必须足够小,以容纳新插入的40条数据和ITL slot   

64.  HELLODBA.COM> exec recruit_insert( 5300+1, 'A', 5300+40 );   

65.    

66.  --会话1中执行,造成ITL等待,由于其已经对T_TEST5的一条数据进行UPDATE造成会话2的请求等待,因此形成死锁   

67.  HELLODBA.COM> insert into tx_test_itl(a, b, c) values (5360, 'A', sysdate);   

68.  insert into tx_test_itl(a, b, c) values (5360, 'A', sysdate)   

69.              *   

70.  ERROR at line 1:   

71.  ORA-00060: deadlock detected while waiting for resource  

    然后,我们对生成的TRACE文件进行深入分析。

    首先看到死锁链:

SQL代码

1.    Deadlock graph:   

2.                           ---------Blocker(s)--------  ---------Waiter(s)---------   

3.    Resource Name          process session holds waits  process session holds waits   

4.    TX-00180012-0000027b        21     311     X             22     295           X   

5.    TX-005e0017-00000051        22     295     X             21     311           S  

    被阻塞会话是311,发生死锁时正在运行的语句是:

SQL代码

1.    *** SESSION ID:(311.277) 2009-09-22 09:28:38.322   

2.    DEADLOCK DETECTED   

3.    [Transaction Deadlock]   

4.    Current SQL statement for this session:   

5.    insert into tx_test_itl(a, b, c) values (5360, 'A', sysdate)  

    找到被阻塞的语句对于我们确定发生ITL等待的对象很重要。

    阻塞会话是295,找到其事务地址:

SQL代码

1.    (session) sid: 295 trans: 1C7B729C, creator: 1F7CE4F8, flag: (100041) USR/- BSY/-/-/-/-/-   

2.              DID: 0001-0016-00000039, short-term DID: 0000-0000-00000000   

3.              txn branch: 00000000   

4.              oct: 6, prv: 0, sql: 1AD1C424, psql: 1F18B8DC, user: 35/DEMO  

    其事务地址为1C7B729C,然后由此地址在Trace文件中找到这个事务下面的队列信息,其中TX队列(type: 39)是我们感兴趣的:

SQL代码

1.    SO: 1DECF7B0, type: 39, owner: 1C7B729C, flag: -/-/-/0x00   

2.    (List of Blocks) next index = 6   

3.    index   itli   buffer hint   rdba       savepoint    

4.    -----------------------------------------------------------   

5.        0      3   0x15bf86fc    0x200d9b2    0x443   

6.        1      4   0x133fa53c    0x141087e    0x445   

7.        2     38   0x10fef32c    0x1401f5f    0x446   

8.        3     41   0x1701576c    0x1401e0d    0x44b   

9.        4      2   0x107ddeec    0x3c00199    0x44e   

10.      5     41   0x170100bc    0x2402b88    0x450  

    可以看到,这里列出了事务所作用到的所有数据块。结合之前找到的被阻塞的语句,我们知道等待是发生在tx_test_itl或者其索引上。我们看下这些数据块分别是属于哪些对象:

SQL代码

1.    HELLODBA.COM> select owner, segment_name from dba_extents   

2.      2  where file_id = dbms_utility.data_block_address_file(TO_NUMBER('200d9b2''XXXXXXXX'))   

3.      3  and dbms_utility.data_block_address_block(TO_NUMBER('200d9b2''XXXXXXXX')) between block_id and block_id+blocks;   

4.      

5.    OWNER            SEGMENT_NAME   

6.    ---------------- -------------------   

7.    DEMO             T_TEST1   

8.    ... ...  

    最终得知0x1401e0d是表tx_test_itl的数据块,0x3c00199是索引tx_test_itl_idx1的数据块,0x2402b88是索引tx_test_itl_idx2的数据块,这3个数据块是我们感兴趣的块。我们之前说过,数据块上的ITL slot被分配就不会被回收了,而索引数据块如果发生分裂也会继承原有数据块上ITL slot。因此我们可以通过将这3个数据块dump出来判断是哪个数据块上发生的ITL等待(按照先索引再表的顺序,因为索引只有达到最大限制和分裂事务ITL slot争用时才发生ITL等待,如果索引块这两个条件都没有满足,可以判断是表的数据块发生ITL等待)。最终,我们会发现是0x3c00199上达到ITL slot上限(2k数据块,上限为41):

SQL代码

1.     Object id on Block? Y   

2.     seg/obj: 0x3078e  csc: 0x00.b0e1e2b9  itc: 41  flg: E  typ: 2 - INDEX  

3.         brn: 0  bdba: 0x3c00182 ver: 0x01 opc: 0   

4.         inc: 0  exflg: 0   

5.        

6.     Itl           Xid                  Uba         Flag  Lck        Scn/Fsc   

7.    0x01   0x0008.02b.00020f75  0x00801eeb.76b3.09  CB--    0  scn 0x0000.b0e11508   

8.    0x02   0x0035.027.00000061  0x0080033e.0056.03  C---    0  scn 0x0000.b0e1ddd8   

9.    ... ...   

10.  0x29   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000  

    此外,还有一个思路:通过被阻塞事务的队列信息找到其TX队列中的加锁数据块:

SQL代码

1.    SO: 1DED2DB0, type: 39, owner: 1DF1E5F4, flag: -/-/-/0x00   

2.    (List of Blocks) next index = 2   

3.    index   itli   buffer hint   rdba       savepoint    

4.    -----------------------------------------------------------   

5.        0      2   0x133eca9c    0x14103d7    0x114   

6.        1      2   0x137ee82c    0x1401e12    0x11c  

    可以看到,这2个数据块分别属于表T_TEST5tx_test_itl,而被阻塞的语句是对tx_test_itl插入一条数据,而此时表中已经插入数据并产生了UNDO数据,说明表tx_test_itl上并没有发生等待,再比较阻塞事务中的数据块信息,可以判断ITL等待是发生在索引上面。

解决方法

    系统中存在少量的ITL等待是正常的,只有当其对系统造成了影响(如awr report中,在top 5 events中发现该事件),或者对应用造成了直接影响(如死锁,再如发现某一会话存在大量等待ITL),我们才需要采取相应手段进行处理。针对导致ITL等待不同原因,我们要采取不同的手段来处理。

INITRANS不足

    这种情况只会出现的表的数据块上,如我们上述的例子:数据块上的ITL数量并没有达到MAX TRANS的限制,可用空间小于24字节。发生这种情况的表通常会被经常UPDATE,从而造成预留空间(PCTFREE)被填满。如果我们发现这类ITL等待对系统已经造成影响,可以通过增加表的INITRANS或者PCTFREE来解决(视该表上的并发事务量而定,通常,如果并发量高,建议优先增加INITRANS,反之,则优先考虑增加PCTFREE)。

    要注意的一点是,如果是使用ALTER TABLE的方式修改这2个参数的话,只会影响新的数据块,而不会改变已有数据的数据块——要做的这一点,需要将数据导出/导入、重建表。

MAXTRANS不足

    这一情况是由高并发引起的:同一数据块上的事务量已经超出了其实际允许的ITL(如前所述,ITL slot所占空间不能超过数据块大小的一半,如8K的限制为169)。因此,要解决这类问题就需要从应用着手,减少事务的并发量;长事务,在保证数据完整性的前提下,增加commit的频率,修改为短事务,减少资源占用事件。而对于OLAP系统来说(例如,其存在高并发量的数据录入模块),可以考虑增大数据块大小。
 

递归事务ITL争用

      这一类等待通常是系统存在并发事务频繁插入、修改数据导致,其往往伴随"enq: TX - index contention"事件出现。根本解决方法就是要减少索引分裂,如使用大数据块、减少索引中效率低、使用率低的字段等。

转载于:https://my.oschina.net/u/729507/blog/102227

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值