推薦:深入研究ITL阻塞與ITL死鎖(轉載)
首先要說的是,這個問題當初我是真遇到過,然後,就有了汪海的這篇文章:
但是NinGoo 在下面回覆說,10g模擬不到這樣的情況了,開始我沒有太在意,後來再想想,9i這麼做的確是不對的,因為還有其它的程式可以釋放資源,根本還沒有達到死鎖的條件。那麼,10g就沒有itl死鎖了嗎?也不是的,10g也有,不過是改進了一下,需要把所有的程式阻塞住的時候,才能爆發出死鎖。
從死鎖的原理上來看,10g是正確的,9i是欠完善的。我們現在完整的分析一下itl等待,以及itl死鎖的前因後果,因為這部分在我的新書中也有涉及,屬於比較難的一部分,先透露出來,免得大家到時候看書可能看得比較糊塗。
1、什麼是ITL
ITL(Interested Transaction List)是Oracle資料塊內部的一個組成部分,用來記錄該塊所有發生的事務,一個itl可以看作是一個記錄,在一個時間,可以記錄一個事務(包括提交或者未提交事務)。當然,如果這個事務已經提交,那麼這個itl的位置就可以被反覆使用了,因為itl類似記錄,所以,有的時候也叫itl槽位。
如果一個事務一直沒有提交,那麼,這個事務將一直佔用一個itl槽位,itl裡面記錄了事務資訊,回滾段的入口,事務型別等等。如果這個事務已經提交,那麼,itl槽位中還儲存的有這個事務提交時候的SCN號。如dump一個塊,就可以看到itl資訊:
Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0006.002.0000158e 0x0080104d.00a1.6e --U- 734 fsc 0x0000.6c9deff0 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
對於已經提交的事務,itl槽位最好不要馬上被覆蓋,因為一致性讀可能會用到這個資訊,一致性讀的時候,可能需要從這裡獲得回滾段的入口,並從回滾段中獲得一致性讀。
itl的個數,受引數initrans控制,最大的itl個數,受maxtrans控制,在一個塊內部,預設分配了2個或3個itl的個數,如果這個塊內還有空閒空間,那麼Oracle是可以利用這些空閒空間並再分配itl的。如果沒有了空閒空間,那麼,這個塊因為不能分配新的itl,所以就可能發生itl等待。
如果在併發量特別大的系統中,最好分配足夠的itl個數,其實它並浪費不了太多的空間,或者,設定足夠的pctfree,保證itl能擴充套件,但是pctfree有可能是被行資料給消耗掉的,如update,所以,也有可能導致塊內部的空間不夠而導致itl等待。
2、ITL等待
我們看一個ITL等待的例子:
- Piner@10gR2>create table test(a int) pctfree 0 initrans 1;
- Table created.
我們這裡指定pctfree為0,initrans為1,就是為了更觀察到itl的真實等待情況,那麼,現在,我們個這些塊內插入資料,把塊填滿,讓它不能有空間分配。
- Piner@10gR2>begin
- 2 for i in 1..2000 loop
- 3 insert into test values(i);
- 4 end loop;
- 5 end;
- 6 /
- PL/SQL procedure successfully completed.
- Piner@10gR2>commit;
- Commit complete.
我們再檢查資料填充的情況:
- Piner@10gR2>select 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 734
- 1 29691 734
- 1 29692 532
可以發現,這2000條資料分佈在3個塊內部,其中有2個塊添滿了,一個塊是半滿的。我們dump一個滿的塊,可以看到itl資訊:
- Piner@10gR2>alter system dump datafile 1 block 29690;
回到os,在udump目錄下,檢查跟蹤檔案,可以看到如下的資訊
Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0006.002.0000158e 0x0080104d.00a1.6e --U- 734 fsc 0x0000.6c9deff0 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
發現,採用如上引數建立的表,塊內部預設有2個itl槽位,如果這裡不指定initrans 1,預設是有3個itl槽位的。
因為只有2個ITL槽位,我們可以用三個會話來模擬等待:
會話1,我們更新這個塊內部的第一行:
- Piner@10gR2>update 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行:
- Piner@10gR2>update 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.
會話3(SID=153),我們更新這個塊內部的第三行,發現被阻塞:
- Piner@10gR2>update test set a=a
- 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
- 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;
可以看到,會話被阻塞
觀察這個時候的等待事件,我們可以發現是ITL等待:
- Piner@10gR2>select EVENT from v$session_wait where sid=153
- EVENT
- ----------------------------
- enq: TX - allocate ITL entry
因為該塊只有2個itl槽位,而現在發生了3個事務,而且,因為該塊被資料添滿,根本沒有剩餘的空間來分配新的itl,所以發生了等待。如果我們這個實驗發生在半滿的塊29692上面,就發現程式3不會被阻塞,因為這裡有足夠的空間可以分配新的itl。
3、ITL死鎖
那麼,理解了itl的阻塞,我們也就可以分析itl的死鎖了,因為有阻塞,一般就能發生死鎖。還是以上的表,因為有2個itl槽位,我們需要拿2個滿的資料塊,4個程式來模擬itl死鎖:
會話1
- Piner@10gR2>update 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
- Piner@10gR2>update 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.
會話3
- Piner@10gR2>update 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.
會話4
- Piner@10gR2>update 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個itl槽位給消耗光了,現在的情況,就是讓他們互相鎖住,達成死鎖條件,回到會話1,更新塊2,注意,以上4個操作,包括以下的操作,更新的根本不是同一行資料,主要是為了防止出現的是TX等待。
- Piner@10gR2>update test set a=a
- 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
- 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;
發現被阻塞
那我們在會話3,更新塊1,當然,也不是同一行
- Piner@10gR2>update test set a=a
- 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
- 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;
被阻塞
注意,如果是9i,在這裡就報死鎖了,在程式1,我們可以看到
- Piner@9iR2>update 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)=29691
- 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,注意,我們也不是更新的同一行資料
- Piner@10gR2>update test set a=a
- 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
- 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;
被阻塞
還有最後一個程式,程式4,我們也不更新同一行資料
- Piner@10gR2>update test set a=a
- 2 where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
- 3 and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;
雖然,以上的每個更新語句,更新的都不是同一個資料行,但是,的確,所有的程式都被阻塞住了,那麼,死鎖的條件也達到了,馬上,我們可以看到,程式1出現提示,死鎖:
- Piner@10gR2>update 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)=29691
- and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3
- *
- ERROR at line 1:
- ORA-00060: deadlock detected while waiting for resource
4、ITL等待與死鎖的避免
為了避免以上的情況發生,我們一定要注意在高併發環境下的表中,正確的設定itl個數,如4個,8個等等,保證該塊有足夠的itl槽位,保證事務能順利的進行,而沒有itl的等待。關於itl的等待,在statspack的段報告中,也能很明顯的看到:
Top 5 ITL Waits per Segment for DB: TEST Instance: test Snaps: 13013 -13014 -> End Segment ITL Waits Threshold: 100 Subobject Obj. ITL Owner Tablespace Object Name Name Type Waits %Total ---------- ---------- -------------------- ---------- ----- ------------ ------- TEST TBS_EL_IND IDX_LLORDER_ORDERID INDEX 3 75.00 TEST TBS_INDEX2 IDX_AUC_FEED_FDATE INDEX 1 25.00
如果出現的頻率很小,象上面的情況,一般可以不用幹預,但是,如果waits很多,則表示這個物件有很嚴重的itl爭用情況,需要增加itl個數。
另外注意的是,有itl等待,並不意味會發生itl死鎖,從上面的例子可以看到,發生itl死鎖的條件還是瞞苛刻的,如果發生了itl死鎖,只能證明,你的系統中,itl等待已經非常嚴重了。
如果想增加initrans個數,引數可以動態修改,但是,只是針對以後的新塊起效,以前的塊如果想生效,需要在新引數下,重整表資料,如重建該表,或者move該表。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/64429/viewspace-916504/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入研究ITL阻塞與ITL死鎖 作者 piner
- 遭遇ITL死鎖
- ITL
- enq: TX - allocate ITL entry等待過多導致全域性死鎖ENQ
- ITL與事務處理
- ITL事務槽記載-01
- oracle ITL槽Oracle
- 【故障處理】佇列等待之TX - allocate ITL entry引起的死鎖處理佇列
- ITL(Interested Transaction List)REST
- ITL的含義
- zt_ITL Cleanout和ITL中的事務狀態分析
- 表的itl 屬性
- itl競爭模擬與解決系列(二)
- enq: TX - allocate ITL entryENQ
- TX:ITL LOCK(INITRANS,MAXINTRANS)
- 鎖的種類,阻塞,死鎖產生與解決辦法。
- ITL和Freelist的區別
- oracle ITL TX MODE 4問題Oracle
- 從Dump資料塊看ITL
- [翻譯]:SQL死鎖-阻塞探測SQL
- 記一次Oracle死鎖/阻塞排查Oracle
- ITL:泰國移動電商的新模式模式
- Oracle ITL (Interested Transaction List) - 事務槽OracleREST
- oracle block資料塊itl小記OracleBloC
- 什麼是Interested Transaction List(ITL)--Oracle?RESTOracle
- 併發程式設計之臨界區\阻塞\非阻塞\死鎖\飢餓\活鎖程式設計
- enq: TX - allocate ITL entry等待事件分析ENQ事件
- zt_Orace ITL(Interested Transaction List) 說明REST
- Segments by ITL Waits 問題及解決AI
- 從ITL到Undo前映象提取實驗
- ITL中xid 和 uba的驗證!
- [20210604]索引分裂與 itl ktbitflg.txt索引
- [20180327]行遷移與ITL浪費.txt
- 關於enq: TX - allocate ITL entry等待事件ENQ事件
- 例項詳解 Java 死鎖與破解死鎖Java
- Java 併發程式設計:如何防止線上程阻塞與喚醒時死鎖Java程式設計
- [20160726]行連結行遷移與ITL槽.txt
- itl在被覆蓋之前被儲存了下來!