事务(进程ID)在锁定资源上与另一个进程发生死锁,已被选为死锁牺牲品.重新运行事务



我有一个C#应用程序,它正在使用存储过程将数据插入SQL Server(2008)表中。我正在使用多线程来完成此操作。正在从线程内部调用存储过程。现在,我的存储过程在插入数据时使用"tablock"。在执行此代码时,我收到以下错误:"事务(进程ID)在锁定资源上与另一个进程发生死锁,已被选为死锁牺牲品。请重新运行该事务。"

有人能帮我解决这个问题吗?

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

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

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

例如,如果Proc1先锁定表1,然后锁定表2,但Proc2先锁定表2再锁定表1则可能出现问题。为了避免这个问题,您可以重写任意一个proc以相同的顺序获取锁。

您可以将查询封装在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 

也可以使用诸如UPDLLOCK之类的表提示。

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

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

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

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

  • 按下"新建查询"按钮并写入命令"update statistics tablename"并执行
  • 重新生成非聚集索引

我也遇到了这个问题,所以我在存储过程中的所有联接之后都使用了WITH (NOLOCK),所以它可以工作,我可以解决我的问题。

在我的开发环境中,在Docker容器中运行的Microsoft SQL Server数据库也遇到了类似的问题。重新启动容器解决了问题。

在查询中使用With(NoLock)以避免在插入/更新记录时出现死锁

以下是来自MSDN的S Kumar Dubey 的解决方案

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 In Results,您将获得SPID、DBID、OBJID、INDID,TYPE、RESOURCE、MODE、STATUS现在检查状态列,如果是显示等待然后杀死那个SPID。终止特定SPID执行SP:杀死65(其中65是SPID)

您似乎需要成为SQL server管理员才能解决此问题。

最新更新