Sql-Server

儲存過程本身死鎖,我該如何解決?

  • March 9, 2018

MSSQL 2012。儲存過程在 10 毫秒內使用相同的參數觸發兩次,我們會遇到死鎖。

儲存過程基本上看起來像這樣(不能共享實際程式碼,但這是相關的虛擬碼):

BEGIN TRAN
UPDATE t
SET
   t.Column1 = NewValue1,
   t.Column2 = NewValue2,
   t.Column3 = NewValue3,
   etc for 20 columns...
FROM Table t
   INNER JOIN Results r
       ON r.ID = t.ResultID
WHERE 
   UserID = @userid
   AND Deleted = 0

UPDATE t
   SET Deleted = 1
FROM Table t
   LEFT JOIN Results r
       ON r.ID = t.ResultID
WHERE
   t.UserID = @userid
   AND t.Deleted = 0
   AND r.ID IS NULL
COMMIT TRAN

Table發生死鎖,涉及的索引是UserID(非聚集覆蓋索引)和主鍵。當我在測試環境中執行該過程時,執行計劃顯示兩個 UPDATE 都使用覆蓋索引來獲取行(查找,不查找),然後必須更新 PK 和覆蓋索引。

我想到的可能的解決方案是:

  1. 刪除顯式事務控制。由於這兩個更新並不真正相互依賴,這似乎是可行的。這會解決僵局嗎?
  2. 刪除 PK 並使 UserID 成為(非唯一)聚集索引。但是,這可能會影響在其他地方使用 PK 的其他事物。
  3. 在第二個 UPDATE 中實現一些布爾邏輯以避免不必要地執行它。但實際上並沒有解決問題,只是減少了它發生的頻率。

我的 dba 職業生涯大約有 2 個月,而僵局很難讓我明白。任何建議將不勝感激!

update在不同事務中執行時,無論是顯式還是隱式,您的 2 個語句中的每一個都可能導致死鎖。它們都更改了同一組行,並且不保證行鎖定的順序。

一種選擇是擷取錯誤並重新執行失敗的事務。另一種方法是以某種方式“序列化”更新。

在我看來,最簡單和最快的方法是修改程序,使其等到同一程序的另一個實例完成更新。例如,它可以通過使用sp_getapplock來實現。

另一種選擇是確保以相同的順序鎖定行(例如 -open 游標,它以特定(唯一)順序讀取所有必需的行,遍歷它,並針對每個行發出SELECT WITH ROWLOCK),並update在所有行被鎖定後發出。然後在同時執行的情況下,第二個事務將等待第一個事務。我認為對於 SQLServer 來說,由於潛在的鎖升級,死鎖的可能性很小。

引用自:https://dba.stackexchange.com/questions/151692