《深入研究itl阻塞与itl死锁》由会员分享,可在线阅读,更多相关《深入研究itl阻塞与itl死锁(5页珍藏版)》请在金锄头文库上搜索。
1、深入研究深入研究 ITL 阻塞与阻塞与 ITL 死锁死锁1、什么是、什么是 ITLITL(Interested Transaction List)是 Oracle 数据块内部的一个组成部分,用来记录该块所有发生的事务,一个 itl 可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务) 。当然,如果这个事务已经提交,那么这个 itl 的位置就可以被反复使用了,因为 itl 类似记录,所以,有的时候也叫 itl 槽位。如果一个事务一直没有提交,那么,这个事务将一直占用一个 itl 槽位,itl 里面记录了事务信息,回滚段的入口,事务类型等等。如果这个事务已经提交,那么,itl
2、 槽位中还保存的有这个事务提交时候的 SCN号。如 dump 一个块,就可以看到 itl 信息:Itl Xid Uba Flag Lck Scn/Fsc0x01 0x0006.002.0000158e 0x0080104d.00a1.6e -U- 734 fsc 0x0000.6c9deff00x02 0x0000.000.00000000 0x00000000.0000.00 - 0 fsc 0x0000.00000000对于已经提交的事务,itl 槽位最好不要马上被覆盖,因为一致性读可能会用到这个信息,一致性读的时候,可能需要从这里获得回滚段的入口,并从回滚段中获得一致性读。itl 的个数
3、,受参数 initrans 控制,最大的 itl 个数,受 maxtrans 控制,在一个块内部,默认分配了 2个或 3 个 itl 的个数,如果这个块内还有空闲空间,那么 Oracle 是可以利用这些空闲空间并再分配 itl。如果没有了空闲空间,那么,这个块因为不能分配新的 itl,所以就可能发生 itl 等待。如果在并发量特别大的系统中,最好分配足够的 itl 个数,其实它并浪费不了太多的空间,或者,设置足够的 pctfree,保证 itl 能扩展,但是 pctfree 有可能是被行数据给消耗掉的,如 update,所以,也有可能导致块内部的空间不够而导致 itl 等待。2、ITL 等待等
4、待我们看一个 ITL 等待的例子:Piner10gR2create table test(a int) pctfree 0 initrans 1;Table created.我们这里指定 pctfree 为 0,initrans 为 1,就是为了更观察到 itl 的真实等待情况,那么,现在,我们给这些块内插入数据,把块填满,让它不能有空间分配。Piner10gR2begin2 for i in 1.2000 loop3 insert into test values(i);4 end loop;5 end;6 /PL/SQL procedure successfully completed.P
5、iner10gR2commit;Commit complete.我们再检查数据填充的情况:Piner10gR2select f,b,count(*) from ( 2 select dbms_rowid.rowid_relative_fno(rowid) f, 3 dbms_rowid.rowid_block_number(rowid) b 4 from test) group by f,b; F B COUNT(*)- - -1 29690 7341 29691 7341 29692 532可以发现,这 2000 条数据分布在 3 个块内部,其中有 2 个块添满了,一个块是半满的。我们 du
6、mp 一个满的块,可以看到 itl 信息:Piner10gR2alter system dump datafile 1 block 29690;回到 os,在 udump 目录下,检查跟踪文件,可以看到如下的信息Itl Xid Uba Flag Lck Scn/Fsc0x01 0x0006.002.0000158e 0x0080104d.00a1.6e -U- 734 fsc 0x0000.6c9deff00x02 0x0000.000.00000000 0x00000000.0000.00 - 0 fsc 0x0000.00000000发现,采用如上参数创建的表,块内部默认有 2 个 itl
7、 槽位,如果这里不指定 initrans 1,默认是有 3 个itl 槽位的。因为只有 2 个 ITL 槽位,我们可以用三个会话来模拟等待:会话 1,我们更新这个块内部的第一行:Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;1 row updated.会话 2,我们更新这个块内部的第 2 行:Piner10gR2update test set a=a 2 where dbms_rowid.ROW
8、ID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;1 row updated.会话 3(SID=153),我们更新这个块内部的第三行,发现被阻塞:Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;可以看到,会话被阻塞观察这个时候的等待事件,我们可以发现是 ITL 等待:Piner10gR2select EV
9、ENT from v$session_wait where sid=153EVENT-enq: TX - allocate ITL entry因为该块只有 2 个 itl 槽位,而现在发生了 3 个事务,而且,因为该块被数据添满,根本没有剩余的空间来分配新的 itl,所以发生了等待。如果我们这个实验发生在半满的块 29692 上面,就发现进程 3 不会被阻塞,因为这里有足够的空间可以分配新的 itl。3、ITL 死锁死锁那么,理解了 itl 的阻塞,我们也就可以分析 itl 的死锁了,因为有阻塞,一般就能发生死锁。还是以上的表,因为有 2 个 itl 槽位,我们需要拿 2 个满的数据块,4 个
10、进程来模拟 itl 死锁:会话 1Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;1 row updated.会话 2Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;1 row updated.会话
11、3Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;1 row updated.会话 4Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;1 row updated.以上 4 个进程把 2 个不同块的 4
12、 个 itl 槽位给消耗光了,现在的情况,就是让他们互相锁住,达成死锁条件,回到会话 1,更新块 2,注意,以上 4 个操作,包括以下的操作,更新的根本不是同一行数据,主要是为了防止出现的是 TX 等待。Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;发现被阻塞那我们在会话 3,更新块 1,当然,也不是同一行Piner10gR2update test set a=a 2 where dbms_ro
13、wid.ROWID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;被阻塞注意,如果是 9i,在这里就报死锁了,在进程 1,我们可以看到Piner9iR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;update test set a=a where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=2
14、9691 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3*ERROR at line 1:ORA-00060: deadlock detected while waiting for resource但是,在 10g 里面,这个时候,死锁是不会发生的,因为这里的进程 1 还可以等待进程 4 释放资源,进程 3 还可以等待进程 2 释放资源,只要进程 2 与进程 4 释放了资源,整个环境又活了,那么我们需要把这两个进程也塞住。会话 2,注意,我们也不是更新的同一行数据Piner10gR2update test set a=a 2 where dbms_rowid
15、.ROWID_BLOCK_NUMBER(rowid)=29691 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;被阻塞还有最后一个进程,进程 4,我们也不更新同一行数据Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;虽然,以上的每个更新语句,更新的都不是同一个数据行,但是,的确,所有的进程都被阻塞住了,那么,死锁的条件也达到了,马上,我们可以看到,进程 1 出现提示,死锁:Piner10gR2update test set a=a 2 where dbms_rowid.ROWID_BLOCK