我在互联网上阅读了大量有关死锁的信息,以确保我不是在问一个愚蠢的问题。发生死锁的下表用作订单队列。实际上没有记录被删除,只有它们的状态发生变化,即 PROCESSING、CANCELLED、COMPLETED。我SELECTs with UPDLOCK
在这张桌子上观察到神秘的死锁。我看到的关于 SELECT 的唯一特点是有一个内部SELECT without UPDLOCK
,但这应该没关系。该表已启用行锁定。这些 SELECTS 由单独的作业调度程序并行运行。这种 SELECT 的含义是它检索具有一个或多个指定状态(上述“正在处理、已取消、已完成”)的第一个(最早)订单。具有最小 ID 的订单(最小 ID 是手动选择的,在下面的查询中为 6850000)被视为“第一”订单。这样做是出于性能原因。这是一个实际的选择:
select * from TORDERSUMMARY with (updlock, rowlock)
where ID in
(select min(ID) from TORDERSUMMARY
where ID > 6850000 and (ORDERSTATUS in ( 'INITIATED' ))
and (ORDERDISPATCHSTATUS in ( 0 , 2 , 4 )) and (ORDERGROUPID is null))
在表中的主键 ID 上定义了一个聚集索引。正如 SQL Server Management Studio 2008 给出的,此类查询的估计和实际执行计划是 CLUSTERED INDEX SEEK。表上有很多非聚集索引,我希望一个NC索引操作和一个聚集索引操作死锁(我在网上详细看过一些这样的案例),但由于只有聚集索引以最佳方式使用(索引搜索),原因似乎并不在那里。
内部 SELECT 没有 UPDLOCK、ROWLOCK 提示,因此不会选择多条记录并进行锁定。我无法想象两个这样的选择会死锁的任何情况。当然,UPDLOCK 提示是因为代码随后会尝试更新订单的状态(将其标记为已处理等)。
这是来自 SQL Server Management Studio 2008 的一个死锁 XDL 文件 - http://pastebin.com/ugCUbn80。服务器本身是 9.0.4035。SQL 查询很长,因此由于某种原因它们在 XDL 文件中被截断,但 SELECT 肯定属于上述类型,因为我已经使用此类 SELECT 对服务器进行了手动压力测试,并且死锁似乎随机发生在同一个方法。