技术开发 频道

事务队列等待深入分析:ITL争用

  【IT168 技术文档】 我们知道,事务在对数据块中的记录加锁时,需要首先在数据块头部记录下该事务的相关信息,这样一个记录就是一条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:

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

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

  
index created.

  HELLODBA.COM
> begin

  
2 for i in 1..5000 loop

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

  
4 end loop;

  
5 end;

  
6 /

  PL
/SQL procedure successfully completed.

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

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

  
363 rows updated.

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

  
1 row updated.

  HELLODBA.COM
> commit;

  
Commit complete.

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

...

  fsbo
=0x2e8

  fseo
=0x30d

  
0x30d - 0x2e8 = 25d

  ...

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

 --Session 1:

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

  
1 row updated.

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

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

  
1 row updated.

  
--Session 2:

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

  
1 row updated.

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

  
1 row updated.

  
--Session 3:

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

  
1 row updated.

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

  
1 row updated.

  
--第四个事务被阻塞:

  HELLODBA.COM
> update tx_lock_tab set c=lpad('1',10,'1') where a=3638;
0
相关文章