我有3个存储过程(简化,请尝试忽略为什么我要更新表两次以及为什么SP被调用两次):
CREATE SP1 AS
BEGIN TRANSACTION
-- Updated twice
UPDATE Customers SET Name = 'something' Where Id = 1 OUTPUT INSERTED.*
UPDATE Customers SET Name = 'something'
COMMIT TRANSACTION;
END
CREATE SP2 AS
BEGIN TRANSACTION
UPDATE Customers SET Name = 'anothername'
COMMIT TRANSACTION;
END
CREATE SP3 AS
BEGIN TRANSACTION
-- Called twice
EXEC SP2
EXEC SP2
COMMIT TRANSACTION;
END
问题是我从sql server遇到了死锁。它说SP1和SP3都在等待Customers表资源。是否有意义?可能是因为SP2中的内部事务?或者可能使用OUTPUT语句......?
锁定是客户PK的钥匙锁。每个等待SP的请求锁定模式是U,所有者是X(我猜的另一个对象)。
更多细节: 1.这些是在同一用户的不同进程中多次调用的。 2.仅为了示例,语句被调用两次。 3.在我的实际代码中,客户实际上被称为“待处理指令”。每个听众(计算机,实际)每分钟对指令表进行采样。 4.第一个更新查询首先获取所有待处理指令,第二个更新查询将整个表的状态更新为完成,以确保没有任何处于待处理模式。 5. SP3两次调用SP2,因为它更新了2个专有指令行,这种情况每天发生一次。
非常感谢!!
答案 0 :(得分:3)
为什么你对此感到惊讶?你已经把书籍案例写成了僵局,然后点击它。
第一个更新查询首先获取所有待处理指令,第二个更新查询将整个表的状态更新为完成。
是的,这将陷入僵局。两个并发调用将找到不同的“待处理”指令(因为可以在其间插入新的“待处理”指令)。然后他们将继续尝试更新整个表并阻塞彼此,死锁。这是时间表:
customer:1
,待处理customer:1
customer:2
,等待customer:2
我有个好消息:僵局是你可以获得的最佳结果。更糟糕的结果是当您的逻辑错过了“待定”客户时(这种情况会更频繁地发生)。简单地说,你的SP1会错误地将第一次更新后插入的任何新“待定”客户标记为“已处理”,而实际上只是跳过了。这是时间表:
customer:1
,待处理customer:1
customer:2
,等待customer:2
处于待处理状态,并且已被重置,但实际上已经处理完毕(不是我的SP1结果集)。所以我建议你回到绘图板并正确设计SP1。我建议SP1应该只在第二个语句上更新它在第一个语句上更新的内容,例如。使用适当的DDL发布真实代码将会有助于获得有用的解决方案。