從更新到意圖排他的PAGELOCK轉換死鎖
我們的一個客戶經常遇到死鎖。死鎖大多在同一個
UPDATE
語句上。死鎖遵循一種模式,其中兩個 SPID 都已獲取頁面上的更新 (U) 鎖,並且都嘗試將 U 頁面鎖上轉換為意向排他 (IX) 鎖。有時只涉及一頁;有時幾個。我們使用跟踪標誌 1222擷取了死鎖跟踪。SQL Server 日誌顯示了許多具有以下模式的死鎖(按從下到上的順序):
waiter id=processe0dc2088 mode=U requestType=convert waiter id=process2f9db2478 mode=U requestType=convert waiter-list owner id=processe0dc2088 mode=IX owner id=process2f9db2478 mode=IX owner-list pagelock fileid=1 pageid=5794 dbid=2 objectname=tempdb.dbo.Item_Package_Site_Costs_Work id=lock1b22de480 mode=IX associatedObjectId=72057594131775488 resource-list
兩個程序都在執行相同的 UPDATE 語句來在這個 tempdb 表上設置一個標誌。此 tempdb 表包含需要在客戶端呼叫之間保持的資訊,直到客戶端完成。該表有一個相當長的索引,以表示唯一程序 ID 的 GUID 開頭。
我很難理解和模擬這種僵局。我已經用模擬數據嘗試了各種數量的記錄。
我的問題:
為什麼這些程序獲取 U 鎖,然後轉換為 IX? 我希望 DELETE 一開始就獲得 IX 鎖。
如何防止死鎖?
導致死鎖的語句如下。該過程剛剛對單個商店的商品列表進行了成本查找。它試圖指出發現了成本。
請注意,UPDATE 語句中有一個已棄用的 (NOLOCK)。這會是一個促成因素嗎?
UPDATE tempdb..Item_Package_Site_Costs_Work SET ItemPkgSiteCost_VINCostFound = 1, ItemPkgSiteCost_VendCost_Key = SiteCosts_VendCost_Key FROM tempdb..Item_Package_Site_Costs_Work (NOLOCK) INNER JOIN #SiteCosts ON ItemPkgSiteCost_GUID = @ProcGUID AND SiteCosts_Item_Type = 0 -- Standard AND ItemPkgSiteCost_Site_Key = SiteCosts_Input_Site_Key AND ItemPkgSiteCost_Item_Key = SiteCosts_Item_Key AND ItemPkgSiteCost_ItemPkg_Key = SiteCosts_Input_Sel_ItemPkg_Key AND ItemPkgSiteCost_VendItem_Key = SiteCosts_VendItem_Key AND ISNULL(ItemPkgSiteCost_Qty_Recv, 1) = SiteCosts_Input_Qty_Recv
客戶的伺服器@@version是:
Microsoft SQL Server 2005 - 9.00.4035.00 (X64) 2008 年 11 月 24 日 16:17:31 版權所有 (c) 1988-2005 Windows NT 6.1(內部版本 7601:Service Pack 1)上的 Microsoft Corporation 標準版(64 位)
到目前為止,我無法擷取死鎖時使用的查詢計劃,並且我嘗試檢索查詢計劃的正常方法沒有返回任何內容(sys.dm_exec_query_plan和sys.dm_exec_text_query_plan都返回 NULL)。
更新 2013-08-29
客戶安裝了 SQL Server 2005 SP 4,但他們仍然看到此死鎖。我將繼續刪除正在修改的表上已棄用的 (NOLOCK),看看這是否修復了死鎖。
首先,程序持有 IX 並希望轉換為 U。這是預期的查詢中的提示。
兩個查詢都將在選擇期間獲取 IX 鎖,然後在需要發生更改(如果有)時轉換為 U。
這可以通過添加: WITH (XLOCK) 作為臨時表的提示來解決。