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事務槽是數據塊頭中用來標記事務的記錄。在這里有個重點是\color{red}{數據塊}。想一想,如果\color{red}{事務跨數據塊}了會怎樣。這就是這個死鎖的關鍵點。當然不同表的事務肯定跨數據塊了,一個事務即使修改一個表的多條數據也可能跨塊了。\color{red}{這里的情況是,order表上事務都是通過主鍵來操作的,對于一條數據,要跨越數據塊,行遷移或者行連接會有這種情況。}

  • 簡單說下這兩個情況
    行遷移一般是update后經常出現,比如一個err_mesg字段,初期只有10個字符,后面update為1000個字符,如果這個時候原數據塊裝不下了,他就會找另外的數據塊來存放,而原數據塊上放一個新數據塊的dba(data block address),指向新的數據塊,如下圖:


    image.png

行連接一般是insert時出現的,比如一條數據非常大,大到一個塊裝不下了,oracle會拆分成多個塊來存放。可以通過創建塊尺寸小的表空間來測試。

到此處,\color{red}{我們要明白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置為1
create 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的鎖
  --形成了互相等待,閉環,死鎖形成
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370