2016-11-14 145 views
2

我有一個應用程序連接到一個SQL Server 2014數據庫,它將幾行組合成一個。應用程序運行時沒有其他數據庫連接。防止SQL Server中的死鎖

首先,在特定時間範圍內選擇一大塊行。此查詢使用與聚簇查找合併的非聚簇查找(TIME列)。

select ... 
from FOO 
where TIME >= @from and TIME < @to and ... 

然後,我們在c#中處理這些行,並將更改寫入單個更新和多個刪除,每個塊會發生多次。這些也使用非聚集索引查找。

begin tran 

update FOO set ... 
where NON_CLUSTERED_ID = @id 

delete FOO where NON_CLUSTERED_ID in (@id1, @id2, @id3, ...) 

commit 

我在使用多個並行塊運行時出現死鎖。我嘗試使用ROWLOCK作爲updatedelete,但由於某些原因,導致甚至比以前更多的死鎖,即使塊之間沒有重疊。

然後我試着TABLOCKX, HOLDLOCKupdate,但這意味着我不能執行我的select並行,所以我失去了並行的優勢。

任何想法如何我可以避免死鎖,但仍然處理多個平行塊?

在這種情況下,在我的select上使用NOLOCK是否安全?因爲塊之間沒有行重疊?那麼TABLOCKX, HOLDLOCK只會阻止updatedelete,對嗎?

或者我應該接受死鎖會發生並重試我的應用程序中的查詢?

UPDATE(附加信息):所有死鎖迄今已在updatedelete階段發生的事情,沒有在select。如果今天我無法解決這個問題,我會嘗試獲取一些死鎖日誌(之前未啓用正確的跟蹤標誌)。

UPDATE:這些是與ROWLOCK發生死鎖的兩個安排,他們都僅指delete聲明和它使用的非聚集索引。我不確定這些是否與沒有任何表格提示的死鎖一樣,因爲我無法再現這些問題。

Deadlock 1 Deadlock 2

問,如果有什麼事,從.xdl別的需要,我有點厭倦連接整個事情的。

+0

你在選擇過程中是否嘗試過聲稱'UPDLOCK'?這樣,當你更新/刪除應該讓你遠離死鎖時,鎖已經在那裏了。如果可能的話,與我們分享一些死鎖記錄的細節。 – Jens

+1

您可以將所有處理移動到存儲過程嗎?你也可以通過簡單地打開快照隔離來解決這個問題,但這取決於你在做什麼。 –

+0

@Jens很抱歉,我現在無法獲取死鎖日誌。看起來,由於死鎖而失敗的所有線程都處於'update'和'delete'階段,因此更改'select'鎖定不太可能影響這種情況。不幸的是,我沒有任何死鎖日誌,我會看看是否可以獲取爲下一次嘗試啓用的死鎖跟蹤標誌。 @ Nick.McDermaid不幸的是不能使用快照隔離。 –

回答

0

有關死鎖的一般建議:確保您按照相同的順序執行所有操作,即按不同的進程獲取相同順序的鎖。

關於Minimizing Deadlocks,您可以在Microsoft技術文章中找到相同的建議。首先列出的原因很充分。

  • 以相同的順序訪問對象。
  • 避免交易中的用戶交互。
  • 保持交易短和在一個批次。
  • 使用較低的隔離級別。
  • 使用基於行版本控制的隔離級別。
  • 將READ_COMMITTED_SNAPSHOT數據庫選項設置爲ON以啓用讀取已提交的事務以使用行版本控制。
  • 使用快照隔離。
  • 使用綁定連接。從卡託問題後

更新:

以相同的順序將如何獲取鎖在這裏適用?你有什麼建議,他會如何改變他的SQL來做到這一點?

死鎖都是一樣的,不管什麼樣的環境:兩個進程(比如說A & B)獲得多個鎖(比如X & Y)以不同的順序,這樣A正在等待YB正在等待XA持有XB持有Y

它適用於這裏,因爲DELETEUPDATE語句隱含獲得的行或索引範圍或表鎖(這取決於發動機認爲適當)。

您應該分析您的流程,並查看是否有可能以不同順序獲取鎖的場景。如果沒有顯示任何內容,則可以使用analyze deadlocks using the SQL Server Profiler

要跟蹤死鎖事件,請將死鎖圖事件類添加到跟蹤中。此事件類使用關於死鎖中涉及的進程和對象的XML數據填充跟蹤中的TextData數據列。 SQL Server Profiler可以將XML文檔解壓縮到一個死鎖XML(.xdl)文件,您可以稍後在SQL Server Management Studio中查看該文件。您可以配置SQL Server Profiler以將死鎖圖形事件提取到包含所有死鎖圖形事件的單個文件或單獨文件。

+0

如何在同一順序獲取鎖定適用於此?你有什麼建議,他會如何改變他的SQL來做到這一點? – Cato

+0

@Cato在更新後的答案中給出額外的上下文。 –

+0

在問題中增加了死鎖圖表,它們似乎只涉及'delete'語句 –

0

我會在更新事務中使用sp_getapplock以防止此代碼的多個實例並行運行。這不會像表鎖定提示那樣阻止選擇語句。

你還是應該編程重試邏輯,因爲它可能需要一段時間才能獲得鎖,比超時參數長。

這是更新的事務如何被包裹成sp_getapplock

BEGIN TRANSACTION; 
BEGIN TRY 

    DECLARE @VarLockResult int; 
    EXEC @VarLockResult = sp_getapplock 
     @Resource = 'some_unique_name_app_lock', 
     @LockMode = 'Exclusive', 
     @LockOwner = 'Transaction', 
     @LockTimeout = 60000, 
     @DbPrincipal = 'public'; 

    IF @VarLockResult >= 0 
    BEGIN 
     -- Acquired the lock 
     update FOO set ... 
     where NON_CLUSTERED_ID = @id 

     delete FOO where NON_CLUSTERED_ID in (@id1, @id2, @id3, ...) 

    END ELSE BEGIN 
     -- return some error code, so that the caller could retry 
    END; 

    COMMIT TRANSACTION; 
END TRY 
BEGIN CATCH 
    ROLLBACK TRANSACTION; 
    -- handle the error 
END CATCH; 

選擇語句不需要任何更改。

我會建議您對NOLOCK,即使您說大塊中的ID不重疊。通過這個提示,SELECT查詢可以跳過一些正在更改的頁面,它可以讀取一些頁面兩次。這種行爲不可能被容忍。

+0

看起來'sp_getapplock'會比我當前的實現慢,我最好只重試所有的死鎖。感謝有關'NOLOCK'的信息。 –