在执行读取/更新的 MS SQL 存储过程上获取死锁(放置代码来处理死锁)

Posted

技术标签:

【中文标题】在执行读取/更新的 MS SQL 存储过程上获取死锁(放置代码来处理死锁)【英文标题】:Getting deadlocks on MS SQL stored procedure performing a read/update (put code to handle deadlocks) 【发布时间】:2014-03-01 16:12:08 【问题描述】:

我不得不承认我只是在学习如何正确处理死锁,但根据我阅读的建议,我认为这是处理它的正确方法。基本上我有很多进程试图在数据库中“保留”一行以进行更新。所以我首先读取一个可用的行,然后写入它。这不是正确的方法吗?如果是这样,我需要如何修复这个 SP?

CREATE PROCEDURE [dbo].[reserveAccount] 
    -- Add the parameters for the stored procedure here
    @machineId varchar(MAX)
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

BEGIN TRANSACTION;
    declare @id BIGINT;

    set @id = (select min(id) from Account_Data where passfail is null and reservedby is null);

    update Account_data set reservedby = @machineId where ID = @id;

    COMMIT TRANSACTION;
END

【问题讨论】:

【参考方案1】:

您可以将其编写为单个语句。这可能会解决更新问题:

update Account_data
    set reservedby = @machineId
    where ID = (select min(id) from Account_Data where passfail is null and reservedby is null);

【讨论】:

我不敢相信我没有想到它可以而且应该合并成一个这样的声明。非常感谢。 所以在过去的几天里,我一直在尝试这个,看到同样数量的死锁发生了。当这个 SP 被很多进程调用时,我在这里缺少什么导致这么多死锁?任何帮助将不胜感激。 此模式与问题中的原始模式类似,不会阻止竞争条件。两个并发会话可以获取相同的 ID 并继续更新。捕获并发布死锁 XML 以便更好地分析。【参考方案2】:

嗯,你的问题是 2,你有 2 个语句 - 一个选择和一个更新。如果它们同时运行,那么选择将.....做一个读锁,更新将需要一个写锁。同时2台机器死机。

简单的解决方案是使初始选择需求成为 uddate 锁(以 (ROWLOCK, UPDLOCK) 作为提示)。这可能有效,也可能无效(取决于其他情况),但这是一个好的开始。

第二步 - 如果失败 - 是使用应用程序级别锁 (sp_getapplock) 确保关键系统始终只有一个所有者,并且 htus 仅串行执行事务。

【讨论】:

谢谢,是的,我想我误解了。我假设设置我的隔离级别会执行您上面提到的操作,锁定读取行,以便我可以更新它而不会发生任何冲突。 没有。或者 - 这取决于。除了 tx 级别可序列化之外的任何东西都不会。而且你没有设置一个特别好的隔离级别。 ReadCommited 确保我所说的会发生。 @TomTom 在读取已提交隔离级别下,读取不会持有共享锁,直到事务结束。所以读锁会在更新锁请求的时候被释放。

以上是关于在执行读取/更新的 MS SQL 存储过程上获取死锁(放置代码来处理死锁)的主要内容,如果未能解决你的问题,请参考以下文章

MS SQL 批量给存储过程/函数授权

如何删除 MS SQL Server 存储过程中的 While 循环? [关闭]

MS sql如何使用存储过程?

如何使用 VBA 执行插入/更新记录的存储过程?

在 MS Access 中回滚多个 SQL 更新查询

在 SQL Server 上插入更新存储过程