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

问题描述 投票:0回答:7

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

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

c# sql-server multithreading deadlock
7个回答
36
投票

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

有多种方法可以预防,包括:

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

例如如果 Proc1 锁定 table1 然后锁定 table2,但 Proc2 锁定 table2 然后锁定 table1,就会出现问题。您可以重写任一过程以按相同的顺序获取锁以避免此问题。


13
投票

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


1
投票

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

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

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

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

  • 按新查询按钮并写入 命令'update statistics tablename'并执行
  • 重建非聚集索引。

0
投票

我也有这个问题,所以我在所有加入我的存储过程后使用了

WITH (NOLOCK)
,所以它有效,我可以解决我的问题。


0
投票

只是为了其他人的利益需要在这里更新,对我来说这是一个奇怪的错误消息,花了很多时间来解决错误消息。最后在网上得到一条线索,我应该使用查询选项 MAXDOP 1 来避免并行性。当我选择它时,错误消息完全不同。其实是在varchar列插入语句导致的字符串截断,调整了要摄取的字符串长度,一切恢复正常。

希望对某人有帮助。


-3
投票

这是 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

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

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


-4
投票

你可以从锁定对象使用

     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 { }
    }
© www.soinside.com 2019 - 2024. All rights reserved.