Mysql-死鎖

死鎖

死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方佔用的資源,從而導致的惡性循環現象。

MyISAM總是一次獲得所需的全部鎖,要麼全滿足,要麼等待,因此不會出現死鎖。但在Innodb中,除單個SQL組成的事務外,鎖是逐步獲得的,這就決定了Innodb中發生死鎖是可能的。

死鎖出現典型場景

兩個事務都需要獲得對方持有的排他鎖才能繼續完成事務。 where id = 1 for update。一個事務先A表,加鎖,再B表在加鎖,另個事務先B表加鎖,在A表加鎖 發生死鎖後

Innodb一般都會自動檢測到,並使一個事務釋放鎖並回退,另一個事務獲得鎖完成事務。但在涉及外部鎖或表鎖的情況下,Innodb並不能完全自動檢測到死鎖,這需要通過設置鎖等待超時參數innodb_lock_wait_timeout來解決。這個參數並不是只用來解決死鎖的問題的,在併發訪問比較高的情況下,如果大量事務因無法立即獲得鎖需的鎖掛起,會佔用大量計算機資源,造成嚴重性能問題,甚至拖垮數據庫,設置合適的鎖等待超時閥值,可以避免這種情況發生。

事務中死鎖代碼

// 事務1
START TRANSACTION;
UPDATE TABLE DemoA SET NAEME = xxx WHERE ID = 1;
UPDATE TABLE DemoB SET NAEME = xxx WHERE ID = 2;
COMMIT;
// 事務2
START TRANSACTION;
UPDATE TABLE DemoA SET MOUT = xxx WHERE NAME = 2;
UPDATE TABLE DemoB SET MOUT = xxx WHERE NAME = 1;
COMMIT;

 

· 表DemoA、DemoB的ID是主鍵、NAME是普通索引

· 假如恰好兩個事務同時執行,以ID1、2的記錄去更新名稱,ID是主鍵索引(特殊索引),此時會先鎖定主鍵索引,然後去更新名稱,名稱在以上表中處於普通索引,同時也會鎖定NAME的普通索引(因爲要保持主鍵索引的葉子節點數據跟普通索引的節點名稱數據保持一致)。事務2在做更新時,是根據NAME普通索引去更新MOUT字段。事務2會先鎖定NAME普通索引,由於要確定更新的行數,同時會索引NAME普通索引對應的主鍵索引(普通索引葉子節點存放的是主鍵ID,然後根據ID回表查詢更新)。由於兩個事務此時都會請求鎖定對方佔用的資源,但此時兩個事務都無法釋放資源,由此陷入死鎖

· 爲了避免死鎖,事務2可以優化成“SELECT ID FROM DemoB WHERE NAME = 2”;即先查詢出來對應的ID主鍵,然後通過主鍵去更新相應的記錄即可(這樣的好處在於:只鎖定了主鍵ID索引,而避免NAME普通索引同時鎖定)。更新操作時,如果更新非主鍵索引的記錄,建議先通過條件查找對應的主鍵ID,然後再根據主鍵ID去更新記錄,避免死鎖

下面幾個實例介紹幾種避免死鎖的常用方法:

  1. 不同程序會併發存取多個表,應儘量約定以相同的順序訪問,這樣子可以大大降低產生死鎖的機會。事務1:查詢A表加鎖,插入B表,事務2:插入B表,查詢A表加鎖。
  2. 程序以批量處理數據的時候,事先對數據排序,保證每個線程按固定的順序來處理記錄,也可以大大降低出現死鎖。事務1:查詢A表 where id = 1,where id = 3,事務2:查詢A表 where id = 3 ,where id = 1。
  3. 在事務中,如果要更新記錄,應該直接申請足夠級別的鎖,即排他鎖。不應先申請共享鎖,更新時在申請排他鎖。
  4. 在REPEATABLE-READ隔離級別下,如果兩個線程同時對相同條件記錄用了排他鎖,在沒有複合該條件記錄的情況下,兩個線程都會加鎖成功,程序發現記錄尚不存在,就試圖插入一條記錄,如果兩個線程都這麼做,就會出現死鎖,這種情況下,將隔離級別改成READ COMMITED就可避免問題(設置爲READ COMMIT是,兩個線程都會執行排他鎖,判斷記錄是否湊整,如果沒有插入,此時只有一個線程插入成功,另一個線程出現鎖等待,當第一個線程插入成功後,第二個線程會出現主鍵重複錯誤,這個錯誤雖然是線程錯誤,但是卻獲得一個排他鎖,如果這個時候第三個線程去申請排他鎖,則會出現死鎖,因爲第二個線程未解鎖,此時需要在程序捕獲第二個線程插入的主鍵衝突的錯誤,回滾事務並釋放鎖,這樣子第三個線程則可以成功申請排他鎖)

1、3的情況是因爲,防止事務1在處理程序的過程中,已經在處理第n的程序的時候,才發現正要處理的數據已經加鎖,衝突,導致後續的程序無法處理,兩個事務都卡頓了,如果按照順序,這個時候,在剛處理的時候已經發現鎖以被佔用,則不需要花更多的時間去處理可能需要回滾的程序,可以提前回滾事務等。 事務1已經處理到了,where id = 3,這個時候事務2剛開始就使用where id =3 加鎖,因爲鎖是行鎖索引實現的,所以這個時候就衝突了。出入數據也同理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章