2012-02-09 56 views
14

我有一個C#應用程序正在使用存儲過程將數據插入到SQL Server(2008)表中。我正在使用多線程來做到這一點。存儲過程從線程內被調用。 現在我的存儲過程在插入數據時使用「tablock」。 執行此代碼時,出現以下錯誤: 「事務(進程ID)與另一個進程在鎖定資源上死鎖,並被選爲死鎖受害者,重新運行事務。事務(進程ID)在另一進程的鎖資源上死鎖,並被選爲死鎖犧牲品。重新運行

任何人都可以請幫助我解決這個問題嗎?

+2

死鎖,你必須在你的問題中添加存儲過程的代碼,以及你的數據庫架構的相關部分。 – ken2k 2012-02-09 14:07:58

回答

12

這發生在兩個Sql Server進程正在訪問相同的資源時,但順序不同。因此,他們最終都在等待另一個進程,這是一個僵局。

有許多的方法來防止它,其中包括:

  • 避免服用不必要的鎖。查看查詢所需的事務隔離級別,在適當情況下使用with (nolock)鎖定提示進行查詢。
  • 確保在進行鎖定時,您在每個查詢中以相同順序鎖定對象。

例如,如果Proc1鎖定table1,然後鎖定table2,但Proc2鎖定table2,然後鎖定table1,則會出現問題。您可以重寫任一proc以相同的順序鎖定以避免此問題。

2

您可以封裝在一個try catch塊查詢,並捕獲錯誤號(與鎖)

  1. 1204
  2. 1205
  3. 1222

然後你就可以自動重試,達到一定的數量。所以你會做類似以下的事情;

  DECLARE @RetryNo Int = 1 
    ,@RetryMaxNo Int = 5; 
    WHILE @RetryNo < @RetryMaxNo 
     BEGIN 
     BEGIN TRY 

     -- put your query that generates locks here.... 

      SELECT @RetryNo = @RetryMaxNo; 
     END TRY 
     BEGIN CATCH 
      IF ERROR_NUMBER() IN (1204, 1205, 1222) 
       BEGIN 
        SET @RetryNo += 1; 
        -- it will wait for 10 seconds to do another attempt 
        WAITFOR DELAY '00:00:10'; 
       END 
      ELSE 
       THROW; 
     END CATCH 
     END 

您還可以使用表格提示,如UPDLOCK

+0

爲什麼你使用'SELECT @RetryNo = @RetryMaxNo;'而不是'BREAK;'? – Storm 2016-05-12 12:12:28

+0

因爲在那時我們不希望它失敗,所以我們希望在拋出異常(如果它與超時有關)之前重申該過程並給它最大的嘗試次數。 – Mez 2016-05-12 12:36:55

+0

爲什麼'BREAK'使它失敗?它不會拋出異常,因爲它已經傳遞了任何可能使其失敗的代碼,所以'BREAK'只會短路迴路。 – Storm 2016-05-12 14:08:12

0

你可以從鎖對象使用SQL Server上

 static object _lock = new object(); 
    public static void _main() 
    { 
      lock (_lock) 
      { 
       _bulkcopy(myData); 
      } 
    } 
    public static void _bulkcopy(DataTable dt) 
    { 
     try 
     { 
      using (var connection = new SqlConnection(ConfigurationSettings.AppSettings.Get("DBConnection"))) 
      { 
       connection.Open(); 
       SqlTransaction transaction = connection.BeginTransaction(); 

       using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) 
       { 
        bulkCopy.BatchSize = 100; 
        bulkCopy.DestinationTableName = "dbo.MyTable"; 
        try 
        { 
         bulkCopy.WriteToServer(dt); 
        } 
        catch (Exception) 
        { 
         transaction.Rollback(); 
         connection.Close(); 
        } 
       } 

       transaction.Commit(); 
      } 




     } 
     catch { } 
    } 
相關問題