事务(进程ID)在锁资源上与另一个进程死锁,并被选为死锁牺牲品。重新运行该交易

时间:2012-02-09 14:02:59

标签: c# sql-server multithreading deadlock

我有一个C#应用程序,它使用存储过程将数据插入SQL Server(2008)表。我正在使用多线程来做到这一点。正在从线程内部调用存储过程。 现在我的存储过程在插入数据时使用“tablock”。 执行此代码时,我收到以下错误: “事务(进程ID)在锁资源上与另一个进程陷入僵局,并被选为死锁受害者。重新运行该事务。”

任何人都可以帮我解决这个问题吗?

5 个答案:

答案 0 :(得分:13)

当两个Sql Server进程访问相同的资源但以不同的顺序访问时,会发生这种情况。因此他们最终都在等待另一个进程,这是一个僵局。

有很多方法可以阻止它,包括:

  • 避免接受不必要的锁定。查看查询所需的事务隔离级别,在适当的情况下使用with (nolock)锁定提示进行查询。
  • 确保在获取锁定时,您可以在每个查询中以相同的顺序锁定对象。

E.g。如果Proc1锁定table1然后table2,但Proc2锁定table2然后table1,则可能出现问题。您可以重写proc以同一顺序获取锁定以避免此问题。

答案 1 :(得分:4)

您可以将查询封装在TRY CATCH块中,并捕获错误编号(与锁相关)

  1. 1204
  2. 1205
  3. 1222
  4. 然后你可以自动重试,达到一定的数量。所以你会做类似以下的事情;

             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

答案 2 :(得分:0)

您可以使用Lock对象

     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 { }
    }

答案 3 :(得分:0)

请确保您要更新或插入哪个字段,该字段具有非聚集索引。如果不可用,您可以先在此表上创建此字段的非聚集索引,然后按照以下步骤进行创建。

  • 右键单击表并选择属性。

  • 在属性的右侧面板中选择“选项”。

  • 在“锁定”选项卡中,“允许页面锁定”设置为“ False”,“允许行锁定”必须为“ True”,然后按确定。

  • 按“新建查询”按钮并输入 命令“更新统计信息表名”并执行
  • 重建非聚集索引。

答案 4 :(得分:0)

这是S Kumar Dubey提供的MSDN解决方案

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/171d9fa9-0a39-48ce-bc38-35623e0c1075/how-can-i-release-lock-on-tables?forum=transactsql

  

执行SP:SP_LOCK在结果中,您将获得SPID,DBID,OBJID,INDID,   TYPE,RESOURCE,MODE,STATUS现在检查状态列(如果是)   显示等待,然后杀死该SPID。杀死特定的SPID执行   SP:杀死65(其中65是SPID)

看来您需要成为SQL Server管理员才能解决此问题。