1. 程式人生 > 實用技巧 >SQL Server 【提高】 死鎖

SQL Server 【提高】 死鎖

死鎖

在兩個或多個任務中,如果每個任務鎖定了其他任務試圖鎖定的資源,此時會造成這些任務永久阻塞,從而出現死鎖

死鎖發生的原因

  • 系統資源不足

  • 程序執行推進的順序不合適

  • 資源分配不當等

產生死鎖的必要條件

  • 互斥條件:一個資源每次只能被一個程序使用

  • 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放

  • 不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪

  • 迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖

死鎖型別

  • 迴圈死鎖:兩個程序請求不同資源上的鎖,每一個程序都需要對方持有的該資源上的鎖,這時將發生迴圈死鎖

  • 轉換死鎖:兩個或多個程序都在事務中持有同一資源上的共享鎖,並且都想把它升級為獨佔鎖,但是,誰也沒法升級直到其他的程序釋放共享鎖

如何檢視死鎖

  • 執行系統儲存過程

    EXEC sp_who 
    EXEC sp_lock
  • 通過 Sql Profile檢視死鎖資訊

如何處理死鎖

  • SQL Server 自動處理

    SQL Server在後臺程序中實現死鎖檢測稱為死鎖監控(Deadlock Monitor)。這個後臺程序每5秒鐘執行一次,為死鎖檢查當前鎖定情況。在最壞的情況中,因此一個死鎖不應該超過5秒。這個查詢會回滾並收到1205錯誤號。

  • KILL會話

  • 設定鎖請求超時
    預設情況下,資料庫沒有鎖定超時期限。也就是說一個會話在申請新的資源時,如果這個資源已經被其它程序鎖定,那麼本會話會一直處於等待狀態。這樣無疑是有問題的。我們可以通過SQL命令來設定鎖請求超時。也可以訪問全域性變數 @@LOCK_TIMEOUT 來檢視這個值。

SET LOCK_TIMEOUT 20000; --單位是毫秒

當請求鎖超過設定時間時,SQL Server將返回錯誤。我們的程式可以根據返回的錯誤來進行響應的處理,避免長時間的使用者等待。

如何避免死鎖

預防死鎖就是破壞四個必要條件中的某一個和幾個,使其不能形成死鎖。有如下幾種辦法:

  • 破壞互斥條件

    破壞互斥條件有比較嚴格的限制,在SQL Server中,如果業務邏輯上允許髒讀,則可以通過將隔離等級改為未提交讀或使用索引提示。這樣使得讀取不用加S鎖,從而避免了和其它查詢所加的與S鎖不相容的鎖互斥,進而減少了死鎖出現的概率。

  • 破壞請求和等待條件

    這點由於事務存在原子性,是不可破壞的,因為解決辦法是儘量的減少事務的長度,事務內執行的越快越好。這也可以減少死鎖出現的概率。

  • 破壞不剝奪條件

    由於事務的原子性和一致性,不剝奪條件同樣不可破壞。但我們可以通過增加資源和減少資源佔用兩個角度來考慮。

    • 增加資源:比如說通過建立非聚集索引,使得有了額外的資源,查詢很多時候就不再索要鎖基本表,轉而鎖非聚集索引,如果索引能夠"覆蓋(Cover)"查詢,那更好不過。因此索引Include列不僅僅減少書籤查詢來提高效能,還能減少死鎖。

    • 減少資源佔用:比如說查詢時,能用select col1,col2這種方式,就不要用select * .這有可能帶來不必要的書籤查詢

最大限度減少死鎖的方法

  • 儘量使用低隔離級別: 確定事務是否能在更低的隔離級別上執行,執行提交讀取允許事務讀取另一個事務已讀取(未修改)的資料,而不必等待第一個事務完成。使用較低的隔離級別(例如提交讀取)而不使用較高的隔離級別(例如可序列讀)可以縮短持有共享鎖的時間,從而降低了鎖定爭奪
  • 按同一順序訪問物件: 按同一順序訪問物件也就是:第一個事務提交或回滾後,第二個事務繼續進行,這樣不會發生死鎖
  • 保持事務簡短並在一個批處理中: 在同一資料庫中併發執行多個需要長時間執行的事務時通常發生死鎖。事務執行時間越長,其持有排它鎖或更新鎖的時間也就越長,從而堵塞了其它活動並可能導致死鎖。 保持事務在一個批處理中,可以最小化事務的網路通訊往返量,減少完成事務可能的延遲並釋放鎖