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种:
注:尽管有PCTFREE为数据块预留了空间,但是,UPDATE操作可能会将其占用,导致空间不足。
下面的代码模拟第一种情形导致的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 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"事件出现。根本解决方法就是要减少索引分裂,如使用大数据块、减少索引中效率低、使用率低的字段等。
|