檔案版本:8.0
來源:How to Minimize and Handle Deadlocks
上一篇:快照讀與加鎖讀
本篇介紹如何減少死鎖的發生,以及出現死鎖時如何處理,
死鎖指不同的事務因彼此持有對方等待的鎖而不能繼續執行的情形,因雙方都在等待資源釋放,任意一方都不會釋放已有的鎖,
正文
死鎖是事務形資料庫中的經典問題,但只要死鎖的發生不會頻繁到完全不能執行某個事務,那么就不算危險,通常,當事務因死鎖而回滾時,你需要讓你的應用隨時做好重新發送事務的準備,
InnoDB引擎默認使用行鎖,即使在事務中插入或洗掉單行資料,也能觸發死鎖,因為這些操作并不是真正的“原子的”,在插入或洗掉時它們會給行的索引值(可能有多個)上鎖,
在前面的 InnoDB中的鎖 中解釋過:在默認級別
REPEATABLE READ下,InnoDB在搜索和掃描索引時使用鄰鍵鎖,用于避免幻行,此時InnoDB會無視Where條件的過濾,給每個掃描到的索引值及其間隙上鎖,此策略適用于加鎖讀、INSERT、DELETE,但有一個特殊場景只會上記錄鎖不會上間隙鎖:WHERE條件中涵蓋了唯一索引,
通過以下技巧,你可以處理好死鎖,并減少其發生的概率:
-
隨時使用
SHOW ENGINE INNODB STATUS找出最近發生死鎖的原因,以便調整應用規避死鎖, -
如果頻繁的死鎖警告惹人注目,開啟
innodb_print_all_deadlocks選項以收集額外的除錯資訊,在MySQL的錯誤日志中會記錄每次死鎖的資訊,而不僅僅記錄最后一次,完成除錯后關閉這個選項, -
隨時準備好重啟因死鎖而失敗的事務,死鎖并不危險,重試就好了,
-
保持事務的短小精悍,以降低沖突的可能性,
-
做出一系列關聯變更后立即提交事務,以降低沖突的可能性,特別是不要讓有關聯的MySQL會話長時間掛起未提交的事務,
-
如果使用加鎖讀(
SELECT ... FOR UPDATE或SELECT ... FOR SHARE),嘗試使用更低的隔離級別,如READ COMMITTED, -
在同一事務內修改多張表,或一張表內的不同行時,每次以相同的順序執行操作,以便讓事務形成清晰的鎖操作佇列而規避死鎖,例如,將資料庫操作編排為應用內的函式,或呼叫SQL序列(即MySQL函式或存盤程序),避免將類似的INSERT,UPDATE和DELETE操作分散在代碼各處,
-
精心設計表索引,讓查詢掃描更少的行數,也就意味著更少的鎖,使用
EXPLAIN SELECT查看MySQL認為查詢最適合使用的索引, -
減少鎖操作,如果能容忍查詢回傳老快照中的資料,就不要加
FOR UPDATE或FOR SHARE子句,READ COMMITTED級別適合這種場景,因為每次快照讀都會讀取自己的最新快照,
- 如果這些都不起作用的話,使用表鎖來序列化你的事務,要在InnoDB表中正確使用`LOCK TABLES`,需要先執行`SET autocommit = 0` (不要執行`START TRANSACTION`),緊接著執行`LOCK TABLES`,在顯式提交事務之前不要呼叫`UNLOCK TABLES`,舉個例子,當你需要寫入表t1,讀取表t2,你可以這么寫:
SET autocommit=0; LOCK TABLES t1 WRITE, t2 READ, ...; ... 在表t1和t2上操作 ... COMMIT; UNLOCK TABLES;表級鎖防止表中的并發更改,通過犧牲回應度避免了死鎖,
- 另一種序列化事務的方式是創建輔助的“信號量”表,其中只包含一行資料,讓每個事務在訪問其它表之前先更新這一行,這樣,所有事務將按串行方式執行,注意InnoDB的即時死鎖檢測演算法同樣適用于這種場景,因為此處序列化鎖是一個行級鎖,而對于MySQL的表級鎖,必須使用超時方式來解決死鎖,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/235390.html
標籤:MySQL
