如何減小和處理死鎖 - MySQL 8.0官方文檔筆記(四)

文檔版本:8.0
來源:How to Minimize and Handle Deadlocks
上一篇:快照讀與加鎖讀html

本篇介紹如何減小死鎖的發生,以及出現死鎖時如何處理。
死鎖指不一樣的事務因彼此持有對方等待的鎖而不能繼續執行的情形。因雙方都在等待資源釋放,任意一方都不會釋放已有的鎖。mysql

正文

死鎖是事務形數據庫中的經典問題,但只要死鎖的發生不會頻繁到徹底不能執行某個事務,那麼就不算危險。一般,當事務因死鎖而回滾時,你須要讓你的應用隨時作好從新發送事務的準備。算法

InnoDB引擎默認使用行鎖。即便在事務中插入或刪除單行數據,也能觸發死鎖。由於這些操做並非真正的「原子的」,在插入或刪除時它們會給行的索引值(可能有多個)上鎖。sql

在前面的 InnoDB中的鎖 中解釋過:在默認級別REPEATABLE READ下,InnoDB在搜索和掃描索引時使用鄰鍵鎖,用於避免幻行。此時InnoDB會無視Where條件的過濾,給每一個掃描到的索引值及其間隙上鎖。此策略適用於加鎖讀、INSERT、DELETE,但有一個特殊場景只會上記錄鎖不會上間隙鎖:WHERE條件中涵蓋了惟一索引。數據庫

經過如下技巧,你能夠處理好死鎖,並減小其發生的機率:併發

  • 隨時使用SHOW ENGINE INNODB STATUS找出最近發生死鎖的緣由,以便調整應用規避死鎖。函數

  • 若是頻繁的死鎖警告惹人注目,開啓innodb_print_all_deadlocks選項以收集額外的調試信息。在MySQL的錯誤日誌中會記錄每次死鎖的信息,而不單單記錄最後一次。完成調試後關閉這個選項。設計

  • 隨時準備好重啓因死鎖而失敗的事務。死鎖並不危險,重試就行了。調試

  • 保持事務的短小精悍,以下降衝突的可能性。日誌

  • 作出一系列關聯變動後當即提交事務,以下降衝突的可能性。特別是不要讓有關聯的MySQL會話長時間掛起未提交的事務。

  • 若是使用加鎖讀(SELECT ... FOR UPDATESELECT ... FOR SHARE),嘗試使用更低的隔離級別,如READ COMMITTED

  • 在同一事務內修改多張表,或一張表內的不一樣行時,每次以相同的順序執行操做。以便讓事務造成清晰的鎖操做隊列而規避死鎖。例如,將數據庫操做編排爲應用內的函數,或調用SQL序列(即MySQL函數或存儲過程),避免將相似的INSERT,UPDATE和DELETE操做分散在代碼各處。

  • 精心設計表索引。讓查詢掃描更少的行數,也就意味着更少的鎖。使用EXPLAIN SELECT查看MySQL認爲查詢最適合使用的索引。

  • 減小鎖操做。若是能容忍查詢返回老快照中的數據,就不要加FOR UPDATEFOR 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的表級鎖,必須使用超時方式來解決死鎖。
相關文章
相關標籤/搜索