MySQL中死鎖


1 、死鎖的概念

是指兩個或兩個以上的事務在執行過程中,因爭奪資源而造成的一種互相等待的現象。若無外力作用,事務都將無法推進下去,解決死鎖的最簡單問題是不要有等待,任何的等待都轉換為回滾,並且事務重新開始,但在線上環境,這可能會導致並發性能下降,甚至任何一個事務都不能進行,而這所帶來的問題遠比死鎖的問題更嚴重

解決死鎖的問題最簡單的一種方法是超時,當兩個事務互相等待時,當一個等待時間超過設置的某一閾值時,其中一個事務回滾,另一個等待的事務就能繼續運行了,在InnoDB存儲引擎中,參數innodb_lock_wait_timeout用來設置超時時間

超時機制雖然簡單,但是其僅通過超時后對事務進行回滾的方式處理,或者說其是根據FIFO的順序選擇回滾對象,但若超時的事務所占權重比較大,如事務操作更新了很多航,占用了較多的undo log,這是采用FIFO方式,就顯得不合適了,因為回滾這個事務的時間相對另一個事務所占用的時間可能會更多

除了超時機制,當前的數據庫還采用wait-for graph(等待圖)的方式來進行死鎖檢測,較之超時的解決方案,這是一種更為主動的死鎖檢測方式。InnoDB存儲引擎也是采用這種方式。wait-for graph要求數據庫保存以下兩種信息

鎖的信息鏈表

事務等待鏈表

通過上述鏈表可以構造出一張圖。而這個在圖中若存在回路,就代表存在死鎖,因此資源間相互發生等待,在wait-for graph,事務為圖中的節點,而在圖中,事務T1指向T2邊的定義為

事務T1等待事務T2所占用的資源

事務T1最終等待T2所占用的資源,也就是事務之間在等待相同的資源,而事務T1發生在事務T2的后面

看一個例子,當前事務和鎖的狀態如圖

 

在Transaction Wait Lists中可以看到共有4個事務t1、t2、t3、t4,故在wati-for graph中應有4個節點。而事務t2對row1占用X鎖,事務t1對row2占用s鎖.事務t1需要等待t2中row1的資源,因此在wait-for graph中有條從節點t1指向節點t2.事務t2需要等待事務t1、t4鎖占勇row2對象,故此存在節點t2到節點 t1 t4的邊,同樣,存在節點t3到節點t1 t2 t4的邊,因此最終wait-for graph為

如圖,可以發現回路(t1,t2)因此存在死鎖。通過上述介紹,可以發現wait-for graph是一種較為主動的死鎖檢測機制,在每個事務請求鎖並發生等待時都會判斷是否存在回路,若存在則有死鎖,通常來說,InnoDB存儲引擎會選擇回滾undo量最小的事務

wait-for graph的死鎖檢測通常采用深度優先的算法實現,在InnoDB1.2版本之前,都是采用遞歸方式實現,而1.2版本開始,對wait-for graph死鎖檢測進行了優化,將遞歸用非遞歸的方式實現,從而提高了InnoDB存儲引擎的性能

 

2、  死鎖概率

死鎖應該非常少發生,若經常發生,則系統是不可用的。此外,死鎖的次數應該還要少於等待,因為至少需要2次等待才會產生一次死鎖,從數學角度來分析,死鎖發生的概率問題

當前數據庫中有n+1個線程執行,即當前總共有n+1事務,假設每個事務所做的操作相同,若每個事務由r+1個操作組成,每個操作從R行數據中隨機操作一行數據,並占用對象的鎖,每個事務在執行完最后一個步驟釋放鎖占用的所有鎖資源,最后,假設nr<<R即線程操作的數據只占所有數據的一小部分

在上述模型下,事務獲得一個鎖需要等待的概率是多少?當事務獲得一個鎖,其他任何一個事務獲得鎖的情況為

(1+2+3...+r)/(r+1) ≈r/2

 

由於每個操作為從R行數據中取一條數據,每行數據被取到的概率為1/R,因此,事務中每個操作需要等待的概率PW為

PW=NR/2R

 

事務由r個操作鎖組成,因此事務發生等待的概率PW(T)為

PW(T)=1-(1-PW)r≈r*PW≈nr2/2R

 

死鎖是由於產生賄賂,也就是事務互相等待發生的,如果死鎖的長度為2,即兩個等待的節點間發生死鎖,那么其概率為

一個事務發生死鎖的概率≈PW(T) 2/n≈nr4/4R2

 

由於大部分死鎖的長度為2,因此上述的公式基本代表了一個事務發生死鎖的概率。從整個系統來看,任何一個事務發生死鎖的概率為

系統中任何一個事務發生死鎖的概率≈n2r4/4R2

上述公式可以發現,由於nr<<R,因此,發生死鎖的概率是非常低的,同時,事務發生死鎖的概率與以下幾點因素有關:

事務中事務的數量n,數量越多發生死鎖的概率越大
每個事務操作的數量r,每個事務操作的數量越大,發生死鎖的概率越大
操作數據的集合R,越小發生死鎖的概率越大
3、    死鎖示例

如果程序是串行的,不可能發生死鎖,死鎖只存在於並發的情況,而數據庫本身就是一個並發運行的程序,因此可能發生斯說,下面延時了死鎖的一種經典情況,即A等待B,B等待A,這種死鎖被稱為AB-BA死鎖

在上述操作中,會話B中的事務會拋出1213的錯誤,即表示發生死鎖,死鎖的原因是會話A和會話B的資源在互相等待。大多數死鎖InnoDB存儲引擎本身可以偵測到,不需要人為干預,但是在上述例子中,在會話B中的事務拋出死鎖異常后,會話A中馬上得到了記錄為2的這個資源,這其實是因為會話B中的事務發生了回滾,否則會話A中的事務是不可能得到該資源的,InnoDB存儲引擎是不會回滾大部分的錯誤異常,但是死鎖除外。發生死鎖后,InnoDB存儲引擎會馬上回滾一個事務,這點需要注意,因此如果在應用程序中捕獲1213這個錯誤,其實並不需要對其進行回滾

Oracle數據庫中產生死鎖的常見原因是沒有對外鍵添加索引,而InnoDB存儲引擎會自動對其進行添加,因而能夠很好的避免這種情況發生,而人為刪除外鍵上的索引,就會拋出異常

 

>create table p(

a int,

primary key(a)

)engine=innodb;

>create table c(

b int,

foreign key(b)references p(a)

)engine=innodb;

>show index from c;

 

 

此時我們刪除

>drop index b on c  

Error Code: 1553. Cannot drop index 'b': needed in a foreign key constraint

 

可以看到,雖然在建立子表時指定了外鍵,但是InnoDB存儲引擎會自動在外鍵列上建立一個索引b,並且人為的刪除這個列是不允許的

此外還存在另一種死鎖,即當前事務持有了待插入記錄的下一個記錄的X鎖,但是在等待隊列中存在一個S鎖的請求,則可能發生死鎖,來看一個列子

DROP TABLE t;

CREATE TABLE t(

a INT PRIMARY KEY

)ENGINE=INNODB;

INSERT INTO t VALUES(1),(2),(4),(5);

 

表t僅有一列a並插入了4條記錄,運行如下查詢

可以看到,會話A已經對記錄4持有X鎖,但是在會話A中插入記錄3時會導致死鎖發生,這個問題的產生是由於會話B中請求記錄4的S鎖而發生等待,但之前請求的鎖 對於主鍵記錄1 2都已經成功,若在事件點5能插入記錄,那么會話B在獲得記錄4持有的S鎖后,還需要向后獲得記錄3的記錄,這樣顯然不合理。因此InnoDB存儲引擎在這里主動選擇死鎖,而回滾的是undo log記錄大的事務,這與AB-BA的死鎖處理方式又不同

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM