歡迎光臨
每天分享高質量文章

實戰演練丨Oracle死鎖案例分析,看完你就懂了

問題背


 

發生Oracle死鎖的多個行程執行的都是同一個儲存過程,大概程式碼及順序如下:

 

–1.首先透過主鍵order_no鎖住一條訂單

select t.* from order t where t.order_no=’order_no’ for update; 

–2.其次透過主鍵channel_id鎖住一個渠道

select t.* from channel t where t.channel_id=’channel_id’ for update; 

–3.然後透過主鍵order_no對訂單表資料進行修改

update order t set t.order_status=0,t.finish_time=sysdate where t.order_no=’order_no’;

commit;

 

死鎖情況描述


 

session A

–正在執行陳述句3,他處於enq: TX – allocate ITL entry等待

update order t set t.order_status=0,t.finish_time=sysdate where t.order_no=’orderno_a’;

 

session B

–正在執行陳述句2,他處於enq: TX – row lock contention等待

select t.* from channel t where t.channel_id=’ch1′ for update;

 

session C

正在執行陳述句2,他處於enq: TX – row lock contention等待

select t.* from channel t where t.channel_id=’ch1′ for update;

 

可能還會有更多的session處於執行陳述句2,並等待enq: TX – row lock contention的情況,這裡暫時只列3個session,其實2個也夠了,也能形成,只是機率很低。

 

等待鏈 


 

A被C堵塞,C被B堵塞,B被A堵塞

等待鏈分析: 

A執行到陳述句3了,說明主鍵為orderno_a的order資料行鎖和ch1的channel資料行鎖已經獲取到了,而其餘的B和C只能等待該ch1資料的行鎖釋放。 

B和C都執行到陳述句2了,說明他們都獲取到了各自的order資料行鎖,且資料不是orderno_a鎖代表的資料。這點毋庸置疑。

疑問 :A,B,C操作的都是不同的訂單資料行,且都獲取到了各自的行鎖的,為什麼在表order上,還會發生A被C堵塞呢。

 

要知道為什麼有這個疑問,就要先明白,在A執行order的for update時是已經獲取了itl資源的,所以在後來真正update資料時是不應該存在這個等待的enq: TX – allocate ITL entry,因為他已經獲取這個資源了。

 

死鎖分析


 

要分析這個死鎖就要明白等待事件enq: TX – allocate ITL entry所代表的資源itl事務槽的含義。itl事務槽是資料塊頭中用來標記事務的記錄。在這裡有個重點是 資料塊 。想一想,如果 事務跨資料塊 了會怎樣。這就是這個死鎖的關鍵點。當然不同表的事務肯定跨資料塊了,一個事務即使修改一個表的多條資料也可能跨塊了。 這裡的情況是,order表上事務都是透過主鍵來操作的,對於一條資料,要跨越資料塊,行遷移或者行連線會有這種情況。

 

簡單說下這兩種情況 

行遷移一般是update後經常出現,比如一個err_mesg欄位,初期只有10個字元,後面update為1000個字元,如果這個時候原資料塊找不下了,他就會找另外的資料塊來存放,而原資料塊上放一個新資料塊的dba(data block address),指向新的資料塊,如下圖: 行連線一般是insert時出現的,比如一條資料非常大,大到一個塊裝不下了,oracle會拆分成多個塊來存放。可以透過建立塊尺寸小的表空間來測試。

 

到此處, 要明白itl是資料塊上的資源,即使是同一個事務中,如果事務跨資料塊了,當他要修改這個資料塊時,他也需要重新再次在這個新塊上申請itl資源 ,也就是我這裡死鎖中,假設orderno_a資料rowid指向的塊為dba_1,行遷移中指向的塊為dba_2,在最開始for update時獲取的是塊dba_1中的itl資源,當最後真正update資料時,為了保護操作,需要獲取dba_2上的itl資源。而此時,其餘的很多session,比如B,C……N 等等session將塊dba_2上的itl資源耗盡了,那麼session A就處於等待資料塊dba_2上的itl資源的狀態,對應於enq: TX – allocate ITL entry。而其他session將等待session A釋放渠道表資料的鎖。完成了鎖的閉環

 

到此死鎖分析完畢。

 

測試


 

–建立order表,將PCTFREE置為0,INITRANS置為1create table t_order(mesg varchar2(4000)) PCTFREE 0 INITRANS 1;

–建立channel表

create table t_channel(id NUMBER);

–準備資料,對於order表,至少要有兩個塊有資料

–第一個塊的資料,有三條,即a,b,c

insert into t_order select rpad(‘a’,3000,’a’) from dual;

insert into t_itl select rpad(‘b’,1000,’b’) from dual;

insert into t_order select rpad(‘c’,3000,’c’) from dual;

–更改資料b,此時第一個塊裝不下,將會發生行遷移

update t_order set mesg=(select rpad(‘b’,3000,’b’) from dual) where mesg like ‘b%’;

–可以使用以下陳述句分析行遷移的表,只用作測試,線上生產慎用,可以dump第一個資料塊找到,遷移到哪一個dba去了

create table CHAINED_ROWS (

  owner_name        varchar2(30),

  table_name        varchar2(30),

  cluster_name      varchar2(30),

  partition_name    varchar2(30),

  subpartition_name  varchar2(30),

  head_rowid        rowid,

  analyze_timestamp  date

);

analyze table t_order list chained rows;

select * from CHAINED_ROWS;

–繼續插入資料,將遷移後的資料塊資料增加,方便之後for update時消耗這個塊的itl資源

–通常情況,下麵插入的資料就是放在b資料遷移後的資料塊的

insert into t_order select rpad(‘d’,1000,’d’) from dual;

insert into t_order select rpad(‘f’,6000,’f’) from dual;

insert into t_order select rpad(‘g’,300,’g’) from dual;

insert into t_order select rpad(‘h’,100,’h’) from dual;

/*開始模擬死鎖*/

–t1時刻

  –session A 

    select * from t_order where mesg like ‘b%’ for update;

    select * from  t_channel where id=1 for update;

 –t2時刻

  –session B

    select * from t_order where mesg like ‘d%’ for update;

    select * from  t_channel where id=1 for update;–等待session A 釋放

  –其餘session

    select * from t_order where mesg like ‘f%’ for update;

    select * from  t_channel where id=1 for update;–加入該條資料的行鎖等待

    select * from t_order where mesg like ‘g%’ for update;

    select * from  t_channel where id=1 for update;–加入該條資料的行鎖等待

    …..

/*如果這些資料不在b所在的塊,可以透過設定where條件為以下內容來指定更改b遷移後的塊

where DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) = ‘block_no’ 

    and DBMS_ROWID.ROWID_ROW_NUMBER(ROWID) = 1;

–此時session B與其餘session將t_order的第二個塊,即d,f,g,h資料所在的塊的itl耗盡

–t3時刻

  –session A 去更改t_order的資料

    update t_order t set t.mesg=’bbbbb’ where t.mesg like ‘b%’;

  –此時會等待session B及其他session釋放itl資源,而session B及其他session又在等待session A釋放channel的鎖

  –形成了互相等待,閉環,死鎖形成

 

出處:linuxidc.com/

贊(0)

分享創造快樂