Sql-Server
儲存過程本身死鎖,我該如何解決?
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 和覆蓋索引。
我想到的可能的解決方案是:
- 刪除顯式事務控制。由於這兩個更新並不真正相互依賴,這似乎是可行的。這會解決僵局嗎?
- 刪除 PK 並使 UserID 成為(非唯一)聚集索引。但是,這可能會影響在其他地方使用 PK 的其他事物。
- 在第二個 UPDATE 中實現一些布爾邏輯以避免不必要地執行它。但實際上並沒有解決問題,只是減少了它發生的頻率。
我的 dba 職業生涯大約有 2 個月,而僵局很難讓我明白。任何建議將不勝感激!
update
在不同事務中執行時,無論是顯式還是隱式,您的 2 個語句中的每一個都可能導致死鎖。它們都更改了同一組行,並且不保證行鎖定的順序。一種選擇是擷取錯誤並重新執行失敗的事務。另一種方法是以某種方式“序列化”更新。
在我看來,最簡單和最快的方法是修改程序,使其等到同一程序的另一個實例完成更新。例如,它可以通過使用sp_getapplock來實現。
另一種選擇是確保以相同的順序鎖定行(例如 -open 游標,它以特定(唯一)順序讀取所有必需的行,遍歷它,並針對每個行發出
SELECT WITH ROWLOCK
),並update
在所有行被鎖定後發出。然後在同時執行的情況下,第二個事務將等待第一個事務。我認為對於 SQLServer 來說,由於潛在的鎖升級,死鎖的可能性很小。