UPDATE上的SQL Server死锁对于同一个表

时间:2014-02-13 12:48:19

标签: sql-server stored-procedures deadlock database-deadlocks

我有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个专有指令行,这种情况每天发生一次。

非常感谢!!

1 个答案:

答案 0 :(得分:3)

为什么你对此感到惊讶?你已经把书籍案例写成了僵局,然后点击它。

  

第一个更新查询首先获取所有待处理指令,第二个更新查询将整个表的状态更新为完成。

是的,这将陷入僵局。两个并发调用将找到不同的“待处理”指令(因为可以在其间插入新的“待处理”指令)。然后他们将继续尝试更新整个表并阻塞彼此,死锁。这是时间表:

  1. 表包含customer:1,待处理
  2. T1(运行SP1的第一次更新)更新表并修改customer:1
  3. T2会插入新记录customer:2,等待
  4. T3(运行SP1的第一次更新)更新表并修改customer:2
  5. T1(运行SP1的第二次更新)尝试更新所有表,被T3阻止
  6. T3(运行SP1的第二次更新)尝试更新所有表,被T1阻止。的死锁即可。
  7. 我有个好消息:僵局是你可以获得的最佳结果。更糟糕的结果是当您的逻辑错过了“待定”客户时​​(这种情况会更频繁地发生)。简单地说,你的SP1会错误地将第一次更新后插入的任何新“待定”客户标记为“已处理”,而实际上只是跳过了。这是时间表:

    1. 表包含customer:1,待处理
    2. T1(运行SP1的第一次更新)更新表并修改customer:1
    3. T2会插入新记录customer:2,等待
    4. T1(运行SP1的第二次更新)尝试更新整个表。 customer:2处于待处理状态,并且已被重置,但实际上已经处理完毕(不是我的SP1结果集)。
    5. 您的商家失去了更新。
    6. 所以我建议你回到绘图板并正确设计SP1。我建议SP1应该只在第二个语句上更新它在第一个语句上更新的内容,例如。使用适当的DDL发布真实代码将会有助于获得有用的解决方案。