給定兩個已知語句,我如何複製死鎖?
我有兩個語句(對一個表中的每一行進行更新(稱為表 A)和在另一個表上刪除查找表 A 中的行),我知道這會導致偶爾的死鎖。好像在A表的同一個主鍵索引上有一個X鎖和一個U鎖。
我一直在嘗試複製 SQL Server Management Studio 中的死鎖,但失敗了。我應該可以嗎?
另外,delete 語句的效率非常低,我想我可以通過創建一個覆蓋索引來解決這個問題,這意味著上面提到的主鍵索引不再包含在 delete 語句的實際執行計劃中。鑑於兩個語句最終都需要相同的行,這是否會保證沒有死鎖,或者只是通過為 SQL Server 提供不同的數據路徑來減少死鎖發生的機會?
我終於設法得到一個死鎖圖,如下所示:
如果我沒看錯的話,右邊的流程節點在 PK_LoanFacility 上有一個排他鎖,並且正在請求另一個(可能是因為與此流程節點關聯的語句中有兩個更新。第一個將欄位設置為 NULL所有行,然後第二行使用從另一個數據庫中提取的值更新子集)。左側的流程節點在 PK_LoanFacility 上有一個更新鎖,並且正在請求共享鎖。左邊的語句是從子表中刪除一行,並通過 WHERE 子句查找父 id。即 DELETE FROM table where ForeignID = (SELECT ID FROM ParentTable WHERE x = y)。我不確定為什麼這需要更新鎖定 PK_LoanFacility(父表的 PK)。我猜每當你從表中刪除一行時,
如前所述,我相當肯定我可以通過添加一些適當的索引來消除對請求共享鎖的需求,但我仍然有興趣了解正在發生的事情。
沒有什麼可以防止 RDBMS 中的死鎖。但是,您可以通過遵循一些簡單的規則來大大降低發生的機會:
- 確保使用適當的索引對查詢進行了很好的調整。
- 如果您必須對多個表進行並發鎖定,那麼您的所有語句和過程都應該以相同的順序訪問這些表。
- 盡可能縮短交易時間。
這將使您遇到死鎖的可能性大大降低。然而,你不能完全避免它們,所以你的程式碼應該是有彈性的,只是自動等待一個隨機的時間間隔(幾毫秒)然後再試一次。僅當重試三次左右後您仍然無法執行語句時才會記錄錯誤。
現在到你的問題,如何重現死鎖:有幾種類型的死鎖。最簡單的一種是兩個資源的死鎖:
- 連接 A 鎖定資源 1
- 連接 B 鎖定資源 2
- 連接 A 嘗試在資源 2 上獲取衝突鎖定並且必須等待
- 連接 B 嘗試在資源 1 上獲取衝突鎖定並且必須等待
現在兩個連接互相等待導致死鎖。這種類型的死鎖可以通過在兩個連接上一次執行一條語句的程式碼來相當容易地重現。其他死鎖並不總是那麼容易重現,因為例如它們可能只發生在記憶體壓力情況或類似情況下。
獲取有關死鎖的更多資訊的最簡單方法是設置死鎖圖跟踪。死鎖圖包含許多有助於理解擷取的死鎖的資訊。由於您的系統(希望)死鎖相對較少,因此您可能需要執行一段時間的跟踪。但是,如果您只擷取一個事件,它不會使用很多資源。擷取死鎖圖後,將其視為 XML。分析器給出的圖形表示確實只顯示了有限的資訊量,但它是一個很好的起點。
正如 Sebastian 所說,沒有辦法完全消除 SQL Server 中的死鎖。您能做的最好的事情就是減少您的更改,通常是通過縮短您的交易。
您可以通過在每個語句周圍放置事務來重現您的死鎖。
- 在連接 1 開始交易
- 在連接 1 中執行您的刪除腳本
- 在連接 2 開始交易
- 在連接 2 中執行您的其他腳本
如果該命令不起作用,請嘗試其他方式。這裡的重點是打開交易而不是關閉它們。這會強制 SQL 保持鎖處於打開狀態。
您還可以通過查看死鎖圖來查看歷史死鎖(同樣正如塞巴斯蒂安已經說過的那樣)。Jonathan Kehayias 的這篇文章“使用 SQL Server 2008 擴展事件檢索死鎖圖”給出了一些關於如何查找和查看死鎖圖的相當簡單的說明。