此脚本为什么经常导致SQL死锁?

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

我每小时在Azure的Web作业中运行一个后台任务。有时(似乎超过50%的时间,该代码会在此特定代码段上窒息(出现死锁错误):

foreach (var ownerToProcess in activeOwnersWithMessageArchiving)
{
    foreach (var extension in extensions)
    {
        using (var db = new SqlConnection(connectionString))
        {
            db.Execute(@"
UPDATE T_MESSAGESTARTER 
   SET Started=@started,Completed=NULL 
 WHERE OwnerId=@ownerId AND ExtensionId=@extensionId;

if @@ROWCOUNT=0
  INSERT INTO T_MESSAGESTARTER (OwnerId,ExtensionId,Started) 
  VALUES (@ownerId,@extensionId,@started)
", new { ownerId = ownerToProcess, extensionId = extension, started = DateTimeOffset.Now });
        }
    }
}

这是一个简单的更新/插入语句。我“相信”我也在使用行级阻塞。这不在事务内。另外,顶层大约有60个ownerToProcess项目。在内循环中,每个循环中都有5-60个extension项目(在上面的代码中)。这样,每次运行该SQL语句大约需要执行4000次。每个@owner / @extension组合(在WHERE子句中)都是唯一的。

有时它会一直运行到没有错误。但是有时我会在执行SQL语句之一时遇到死锁错误。是什么原因造成的?是因为我在SQL语句中具有UPDATE/INSERT结构?还是Dapper会做些有趣的事情?

另外要注意的是:所讨论的T_MESSAGESTARTER表没有主键。这可能导致此问题吗?

c# sql sql-server dapper database-deadlocks
3个回答
0
投票

不必是PK,但ownerId和extensionId上的复合唯一索引(理想情况下是集群的)将优化更新查询。通过触摸最少的数据量,可以缓解死锁(取决于所涉及的过程)。


0
投票

即使实际未更新任何行(当行不存在时),更新也会锁定“表”。根据并发性,以下最有可能是安全的(如果两个进程永远不会处理相同的所有者和扩展名,那么它将起作用)

IF EXISTS(SELECT ...  FROM T_MESSAGESTARTER WHERE OwnerId=@ownerId....)
BEGIN
UPDATE T_MESSAGESTARTER 
   SET Started=@started,Completed=NULL 
 WHERE OwnerId=@ownerId AND ExtensionId=@extensionId;
END
ELSE 
  INSERT INTO T_MESSAGESTARTER (OwnerId,ExtensionId,Started) 
  VALUES (@ownerId,@extensionId,@started)
END

0
投票

无需在两次foreach迭代中打开连接。

using (var db = new SqlConnection(connectionString))
{
  foreach (var ownerToProcess in activeOwnersWithMessageArchiving)
  {
    foreach (var extension in extensions)
    {

            db.Execute(@"
UPDATE T_MESSAGESTARTER 
   SET Started=@started,Completed=NULL 
 WHERE OwnerId=@ownerId AND ExtensionId=@extensionId;

if @@ROWCOUNT=0
  INSERT INTO T_MESSAGESTARTER (OwnerId,ExtensionId,Started) 
  VALUES (@ownerId,@extensionId,@started)
", new { ownerId = ownerToProcess, extensionId = extension, started = DateTimeOffset.Now });
    }
  }
}

FYI,也存在MERGE语句。这是标准SQL,也可以进行upserts。

© www.soinside.com 2019 - 2024. All rights reserved.