2012-02-09 54 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 { } 
    } 
相关问题