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

vwhgwdsa  于 2023-02-07  发布在  其他
关注(0)|答案(6)|浏览(1974)

我有一个使用存储过程将数据插入SQL Server(2008)表的C#应用程序。我使用多线程来执行此操作。存储过程是从线程内部调用的。现在我的存储过程在插入数据时使用“tablock”。在执行此代码时,我收到以下错误:“事务(进程ID)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。请重新运行该事务。”
有人能帮我解决这个问题吗?

qnzebej0

qnzebej01#

当两个SQL Server进程访问相同的资源,但顺序不同时,就会发生这种情况。因此,它们最终都在等待另一个进程,这就是死锁。
有很多方法可以预防这种情况,包括:

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

例如,如果Proc1先锁定table1,然后锁定table2,但Proc2先锁定table2,然后锁定table1,则会出现问题。您可以重写其中一个proc,使其以相同的顺序获取锁,以避免此问题。

vyu0f0g1

vyu0f0g12#

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

vpfxa7rd

vpfxa7rd3#

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

  • 右键单击表并选择属性。
  • 在属性的右面板中选择选项。
  • 在“锁定”选项卡中,允许页锁定为“False”,允许行锁定必须为“True”,然后按“确定”。
  • 按下“新建查询”按钮,写入命令“更新统计表名称”并执行
  • 重新生成非聚集索引。
irlmq6kh

irlmq6kh4#

我也遇到过这个问题,所以我在我的存储过程中使用了WITH(NOLOCK)after all joins,所以它工作正常,我可以修复我的问题。

dtcbnfnu

dtcbnfnu5#

下面是由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。杀死65(其中65是SPID)
似乎您需要成为SQL Server管理员才能解决此问题。

vyswwuz2

vyswwuz26#

您可以从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 { }
    }

相关问题