在执行读取/更新的 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 存储过程上获取死锁(放置代码来处理死锁)的主要内容,如果未能解决你的问题,请参考以下文章