HelloDBA [English]
搜索Internet 搜索 HelloDBABA
  Oracle技术站。email: fuyuncat@gmail.com  MSN: fuyuncat@hotmail.com   acoug  acoug 

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

[English]

作者: fuyuncat

来源: www.HelloDBA.com

日期: 2009-10-20 07:34:11

分享到  新浪微博 腾讯微博 人人网 i贴吧 开心网 豆瓣 淘宝 推特 Facebook GMail Blogger Orkut Google Bookmarks

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

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

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

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

  • MAXTRANS不足。当受到MAXTRANS限制时,也会发生此类等待。MAXTRANS的默认值是255(10g以后则不能修改这一参数),但是其实际大小是受到块的大小的限制的。ITL Slot占用的空间不会超过块大小的50%(实际上,如数据块为2K,则ITL最多为41,4k数据块的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. ...  

    仅够容纳1个ITL了(24字节),加上数据块上原有了2条ITL 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$session中ROW_WAIT_*字段信息是并不准确,你可以将其作为参考,但是它可能并不是实际请求的对象。以ROW_WAIT_OBJ#为例,它可能为持锁会话中最后一次加锁的对象(如上例,为T_TEST5,并非真正导致等待的对象TX_LOCK_TAB),大多数情况下其值为-1或0。

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

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

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数据块为文件2上4197,UNDO开始记录为1,且只用到一个回滚块。我们将该UNDO块dump出来:

SQL代码
  1. HELLODBA.COM> alter system dump datafile 2 block 4197;   
  2.   
  3. System altered.  

    找到对应的回滚记录。可以看到,从第1(0x1)条记录开始,slot为7(0x07)的记录有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_TAB的UPDATE操作,而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. ...  

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

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

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. ...  

    可以发现itc为3,没有达到max trans的限制,但是其空闲空间只有0xd=13(avsp),不足以容纳1条ITL 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等待出现的时间周期很短暂,我们通过性能视图观察到的这一类等待基本上是由于INITRANS或MAXTRANS引起的。大量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$session中ROW_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.  

    以下的代码模拟了Index的Maxtrans限制而导致死锁(为了减少事务量,我将模拟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_TEST5和tx_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"事件出现。根本解决方法就是要减少索引分裂,如使用大数据块、减少索引中效率低、使用率低的字段等。

      --- Fuyuncat TBC ---

Top

Copyright ©2005,HelloDBA.Com 保留一切权利

申明
by fuyuncat