为什么这个查询会导致死锁?
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)
死锁图添加:
<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>
添加:
感谢Sankar提供的文章,其中包含如何避免此类死锁的解决方案:
- 从读者的投影中消除不必要的列,这样他就不必查找聚集索引
- 将所需列作为包含列添加到非聚集索引以使索引覆盖,再次使读者无需查找聚集索引
- 避免必须维护非聚集索引的更新
在我看来,好像您正试图在同一个语句中并在同一个表上执行 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
我不希望您将这篇文章标记为答案,而是由其他 SQL Server 专家在此分享有关此主题的更多信息。
http://web.archive.org/web/20111222024618/http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/01/reproduce-deadlocks-involving-only-one-table.aspx
http://rusanu.com/2009/05/16/readwrite-deadlock/