MySQL實戰之死鎖與解決方案


  在實際生產中,死鎖並不少見。那么數據庫死鎖的表現是什么?透過現象看本質,死鎖的原因是什么?分析了原因怎樣合理解決又是一個問題。在JMM之Java中鎖概念的分類總結 - 池塘里洗澡的鴨子 - 博客園 (cnblogs.com)中也提到了死鎖的概念,同時總結了死鎖產生的四大必要條件:

    1)互斥條件:一個資源每次只能被一個進程使用。

    2)請求與保持條件:當一個進程因請求資源而被阻塞時,對已獲得的資源不會釋放。

    3)不可剝奪條件:進程已獲得的資源,在未使用完之前不能強行被剝奪。

    4)循環等待條件:若干進程之間形成一個鍾收尾相接的循環等待資源關系。

  數據庫中發生死鎖的場景也逃不脫以上四大,下面介紹幾種數據庫中常見的死鎖現象,並分析其屬於四大必要條件中的哪種情況然后采用更有效的方式解決:

  一、表鎖死鎖

    業務場景用戶A訪問表A(鎖住了表A),然后又訪問表B;另一個用戶B訪問表B(鎖住了表B),然后企圖訪問表A;這時用戶A由於用戶B已經鎖住表B,它必須等待用戶B釋放表B才能繼續,同樣用戶B等用戶A釋放表A才能繼續,這就死鎖就產生了。

      用戶A--A表(表鎖)--B表(表鎖)

      用戶B--B表(表鎖)--A表(表鎖)

    解決方案這種死鎖比較常見,是由於程序的BUG產生的,除了調整的程序的邏輯沒有其它的辦法。仔細分析程序的邏輯,對於數據庫的多表操作時,盡量按照相同的順序進行處理,盡量避免同時鎖定兩個資源,如操作AB兩張表時,總是按先AB的順序處理, 必須同時鎖定兩個資源時,要保證在任何時刻都應該按照相同的順序來鎖定資源。

  二、行級鎖死鎖

    業務場景1如果在事務中執行了一條沒有索引條件的查詢,引發全表掃描,把行級鎖上升為全表記錄鎖定(等價於表級鎖),多個這樣的事務執行后,就很易產生死鎖和阻塞,最終應用系統會越來越慢,發生阻塞或死鎖。

    解決方案1SQL語句中不要使用太復雜的關聯多表的查詢;使用explain“執行計划"SQL語句進行分析,對於有全表掃描和全表鎖定的SQL語句,建立相應的索引進行優化。

    業務場景2兩個事務分別想拿到對方持有的鎖,互相等待,於是產生死鎖。

        

     解決方案2
      1)在同一個事務中,盡可能做到一次鎖定所需要的所有資源
      2)按照id對資源排序,然后按順序進行處理

  三、共享鎖轉為排他鎖

    業務場景事務A 查詢一條紀錄,然后更新該條紀錄;此時事務B 也更新該條紀錄,這時事務B 的排他鎖由於事務A 有共享鎖,必須等A 釋放共享鎖后才可以獲取,只能排隊等待。事務A 再執行更新操作時,此處發生死鎖,因為事務A 需要排他鎖來做更新操作。但是,無法授予該鎖請求,因為事務B 已經有一個排他鎖請求,並且正在等待事務A 釋放其共享鎖。

      事務A: select * from dept where deptno=1 lock in share mode; //共享鎖,1

          update dept set dname='java' where deptno=1;//排他鎖,3

      事務B: update dept set dname='Java' where deptno=1;//由於1有共享鎖,沒法獲取排他鎖,需等待,2

    解決方案

      1)對於按鈕等控件,點擊立刻失效,不讓用戶重復點擊,避免引發同時對同一條記錄多次操作;

      2)使用樂觀鎖進行控制。樂觀鎖機制避免了長事務中的數據庫加鎖開銷,大大提升了大並發量下的系統性能。需要注意的是,由於樂觀鎖機制是在我們的系統中實現,來自外部系統的用戶更新操作不受我們系統的控制,因此可能會造成臟數據被更新到數據庫中。

  減少死鎖的發生可以大大提高生產效率,那么在MySQL數據庫中如何進行死鎖的排查以預防死鎖的發生呢?MySQL提供了幾個與鎖有關的參數和命令,可以輔助我們優化鎖操作,減少死鎖發生。

  1、查看死鎖日志

    通過show engine innodb status\G命令查看近期死鎖日志信息。

    使用方法:1、查看近期死鎖日志信息;2、使用explain查看下SQL執行計划(參考MySQL索引原理之索引分析 - 池塘里洗澡的鴨子 - 博客園 (cnblogs.com))

  2、查看鎖狀態變量

    通過show status like'innodb_row_lock%‘命令檢查狀態變量,分析系統中的行鎖的爭奪情況

      Innodb_row_lock_current_waits:當前正在等待鎖的數量
      Innodb_row_lock_time:從系統啟動到現在鎖定總時間長度
      Innodb_row_lock_time_avg: 每次等待鎖的平均時間
      Innodb_row_lock_time_max:從系統啟動到現在等待最長的一次鎖的時間
      Innodb_row_lock_waits:系統啟動后到現在總共等待的次數
    如果等待次數高,而且每次等待時間長,需要分析系統中為什么會有如此多的等待,然后着
手定制優化。



  


免責聲明!

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



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