为什么这个查询会导致死锁?

gar*_*rik 11 sql-server-2008 deadlock

为什么这个查询会导致死锁?

UPDATE TOP(1) system_Queue SET
  [StatusID] = 2,
  @ID = InternalID
WHERE InternalID IN (
    SELECT TOP 1 
      InternalID FROM system_Queue
    WHERE IsOutGoing = @IsOutGoing AND StatusID = 1
ORDER BY MessageID ASC, InternalID ASC)
Run Code Online (Sandbox Code Playgroud)

添加死锁图:

<keylock hobtid="72057594236436480" dbid="9" objectname="Z.dbo.system_Queue" indexname="PK_system_Queue" id="lock5b25cc80" mode="X" associatedObjectId="72057594236436480">
    <owner-list>
     <owner id="processc6fe40" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="processc7b8e8" mode="S" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057594405453824" dbid="9" objectname="Z.dbo.system_Queue" indexname="IX_system_Queue_DirectionByStatus" id="lock48cf3180" mode="S" associatedObjectId="72057594405453824">
    <owner-list>
     <owner id="processc7b8e8" mode="S"/>
    </owner-list>
    <waiter-list>
     <waiter id="processc6fe40" mode="X" requestType="wait"/>
    </waiter-list>
   </keylock>
Run Code Online (Sandbox Code Playgroud)

添加:

感谢Sankar的文章,其中提供了如何避免此类死锁的解决方案:

  • 从读者的投影中消除不必要的列,这样他就不必查找聚集索引
  • 将所需的列作为包含列添加到非聚集索引中,使索引覆盖,再次使读者不必查找聚集索引
  • 避免必须维护非聚集索引的更新

SQL*_*tar 13

在我看来,好像您试图在同一个语句中对同一个表执行 SELECT 和 UPDATE。

SELECT 在 IX_system_Queue_DirectionByStatus 索引中的值上持有共享锁,并且 UPDATE 需要释放这些锁,然后才能获得将更新主键的排他锁(我猜它是聚集的,也是IX_system_Queue_DirectionByStatus 键值)。

无论如何,我的猜测是这个查询只会在它选择和更新的索引值不冲突的极少数情况下成功。每次执行时是否都会死锁(我认为会)。

这是一个更详细地解释死锁的链接:http : //sqlblog.com/blogs/jonathan_kehayias/archive/2008/07/30/the-anatomy-of-a-deadlock.aspx