共計 4124 個字符,預計需要花費 11 分鐘才能閱讀完成。
這篇文章主要介紹“Oracle 的死鎖分析”,在日常操作中,相信很多人在 Oracle 的死鎖分析問題上存在疑惑,丸趣 TV 小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Oracle 的死鎖分析”的疑惑有所幫助!接下來,請跟著丸趣 TV 小編一起來學習吧!
問題背景描述:
發生死鎖的多個進程執行的都是同一個存儲過程,大概代碼及順序如下:
--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= ch2 for update;
session C
-- 正在執行語句 2,他處于 enq: TX - row lock contention 等待
select t.* from channel t where t.channel_id= ch2 for update;
可能還會有更多的 session 處于執行語句 2,并等待 enq: TX – row lock contention 的情況,這里暫時只列 3 個 session,其實 2 個也夠了,也能形成,只是概率很低。
等待鏈
A 被 C 堵塞,C 被 B 堵塞,B 被 A 堵塞
等待鏈分析:
A 執行到語句 3 了,說明主鍵為 orderno_a 的 order 數據行鎖和 ch2 的 channel 數據行鎖已經獲取到了,而其余的 B 和 C 只能等待該 ch2 數據的行鎖釋放。
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 的鎖
-- 形成了互相等待,閉環,死鎖形成
到此,關于“Oracle 的死鎖分析”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注丸趣 TV 網站,丸趣 TV 小編會繼續努力為大家帶來更多實用的文章!