2015-10-20 213 views
4

我在SQL Server 2012和.NET 4.5.1上使用EntityFramework 6。長事務處理的死鎖

在長時間運行的交易中,第二個用戶發生死鎖。問題是第一個用戶阻止PayrollListHumanResourceID=90FA9981-AFD3-43BF-AD92-AAE5E2A42B5A的記錄,第二個用戶想要爲PayrollListHumanResourceID=6CFE74C3-F180-497C-8DDA-BCA8D075FF59獲取數據。

以下代碼顯示來自SQL事件探查器的實體框架客戶端事務。對於第二個用戶,最後(有時倒數第二個)exec發生死鎖。爲了舉例,我刪除了大多數可以正常工作的函數。我在C#代碼中爲DELETE部分之後和COMMIT之前的第一個用戶放置了一個斷點。第一個用戶具有不同的p__linq值。

set quoted_identifier on 
set arithabort off 
set numeric_roundabort off 
set ansi_warnings on 
set ansi_padding on 
set ansi_nulls on 
set concat_null_yields_null on 
set cursor_close_on_commit off 
set implicit_transactions off 
set language us_english 
set dateformat mdy 
set datefirst 7 
set transaction isolation level read uncommitted 


begin tran; 
(...) 
exec sp_executesql N'DELETE [Extent1] FROM [dbo].[PayrollListErrors] AS [Extent1] WHERE [Extent1].[PayrollListHumanResourceID] = @p__linq__0',N'@p__linq__0 uniqueidentifier',@p__linq__0='6CFE74C3-F180-497C-8DDA-BCA8D075FF59' 
exec sp_executesql N'DELETE [Extent1] FROM [dbo].[PayrollListElementRelations] AS [Extent1] WHERE [Extent1].[PayrollListHumanResourceID] = @p__linq__0',N'@p__linq__0 uniqueidentifier',@p__linq__0='6CFE74C3-F180-497C-8DDA-BCA8D075FF59' 
exec sp_executesql N'DELETE [Extent1] FROM [dbo].[PayrollListElements] AS [Extent1] WHERE [Extent1].[PayrollListHumanResourceID] = @p__linq__0',N'@p__linq__0 uniqueidentifier',@p__linq__0='6CFE74C3-F180-497C-8DDA-BCA8D075FF59' 

commit; 

在具有表鍵和索引的圖像下面。 PayrollListElements table

建立在我的數據庫一般的表是:INT型

  • ID列,與聚集索引(現在已經過時了,對人友好的閱讀),
  • 類型UNIQUEIDENTIFIER的GUID列,PRIMARY KEY,帶非聚簇索引,
  • UNIQUEIDENTIFIER類型的外鍵,帶非聚簇索引(當然指向GUID列),
  • 每個外鍵都有一個索引(除用於記錄信息的2列外,他們沒有使用在這次交易中)。

鎖升級沒有出現在我的例子中。 更改隔離級別不起作用。刪除/由ID列更新該表工作沒有任何僵局:

DELETE [Extent1] FROM [dbo].[PayrollListElements] AS [Extent1] WHERE [Extent1].ID = 30 

問題出現時行正在從被檢查表和外鍵引用刪除。當我從父表中刪除一行時,所有的子引用都被檢查。儘管所有外鍵都有一個非聚集索引,其中一些正在通過索引掃描而不是索引查找進行檢查。如果在此操作期間另一個用戶至少阻止掃描表中的一行 - 刪除操作將被阻止。即使阻塞行沒有刪除數據的引用,也會發生這種情況。 使用FORCESEEK表提示不起作用。

Delete execution plan

Deadlock graph.xdl

Delete execution plan.sql

Trace profiler.trc

+1

捕獲死鎖圖形XML並將其附加到此處。閱讀[在SQL Server中捕獲死鎖](http://www.brentozar.com/archive/2014/06/capturing-deadlock-information/) –

+0

Remus Rusanu - 我剛剛編輯了我的帖子並附加了其他文件,包括死鎖圖表。 – Bruniasty

回答

2

我想你已經正確識別掃描的問題。掃描在有效的可序列化隔離下運行,以便檢查後不會出現新行並違反FK。

即使有可能強制這些檢查使用嵌套循環,我會建議反對。這看起來像一個相當脆弱和手動修復。事實上,我認爲這不是一個完整的解決方案;它只會減少僵局的可能性。即使檢查發現沒有適用的行範圍,鑰匙鎖仍然會在之前的鑰匙上佔用。你無法避免重疊。

到目前爲止,我的最佳想法是實現最可能的死鎖解決方案:一個重試循環,在SqlException.Number == 1205上重試。您必須重試整個交易。這總是有效並且安全。

另一種方法是使用像全局鎖之類的東西來同時不運行潛在的衝突操作。這是核選擇和最後的武器,因爲它破壞了系統這一特定部分的可擴展性。

+0

另一種選擇是使用隊列進行長操作並調度序列化的隊列執行(可選地使用全局/應用程序鎖來確保串行行爲)。這當然只能在用戶不需要立即看到處理結果的情況下使用。 – Arvo

+0

感謝您的回答,但這並不能以便捷的方式解決我的問題。在這個例子中是否可以強制SQL不使用**索引掃描**? **我從表中刪除了外鍵,並且沒有更多的死鎖。**雖然我不滿意這個解決方案,但我無法想象,每個人都有這個問題,並且通過「解決方法」與它一起生活。這意味着SQL Server不會爲具有多線程/多用戶的實際項目中的事務,外鍵和索引提供任何有效的解決方案。 – Bruniasty

+0

您的最終結論有點太深遠了......導致此處掃描的事情是,掃描的表非常小,輸入恰好已經排序並且存在正確排序的索引。這顯然是成本最低的計劃。非常高效的計劃!正如我所解釋的,循環連接不會解決問題。你同意嗎? – usr