【发布时间】:2020-04-08 09:42:12
【问题描述】:
我每小时在 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 项(在上面的代码中)。这使得在每次运行期间执行大约 4000 次此 SQL 语句。每个 @owner/@extension 组合(在 WHERE 子句中)都是唯一的。
有时它会一直运行而不会出错。但有时我会在 SQL 语句的执行中遇到死锁错误。这可能是什么原因造成的?是因为我在 SQL 语句中有UPDATE/INSERT 结构吗?或者 Dapper 会不会做一些有趣的事情?
另外需要注意的是:T_MESSAGESTARTER 表没有主键。会不会导致这个问题?
【问题讨论】:
-
我们需要查看您的死锁图以了解哪些资源处于死锁状态以及原因。不是单凭代码就能确定的。
-
建议:考虑将这两行移到存储过程中,并在存储过程中启用
AUTOCOMMIT_SP_MODE=ON。我敢打赌,它很有可能消除僵局。容易做到;易于经验验证;) -
@DaleK 我不是 SQL 专家,很遗憾我不知道如何生成死锁图。
-
@MattSpinks 如果你在谷歌上获取死锁图,你会发现有一个相当直接的查询来获取它——我只是没有它。
标签: c# sql sql-server dapper database-deadlocks