我试图弄清僵局。有问题的表mytb
仅用于以下查询:
delete mytb with (uplock,holdlock) where key_value=@value
通过 sp_executesql 调用。后面提到的两个进程都使用相同的代码,除了键值。
我正在使用跟踪标志 1222,这是服务器日志中的资源列表部分:
resource-list
objectlock lockPartition=0 objid=1433553235 subresource=FULL dbid=16 objectname=mydb.dbo.mytb id=lock5620a3480 mode=IX associatedObjectId=1433553235
owner-list
owner id=process4bd94c8 mode=IX
waiter-list
waiter id=process4c85288 mode=X requestType=convert
objectlock lockPartition=0 objid=1433553235 subresource=FULL dbid=16 objectname=mydb.dbo.mytb id=lock5620a3480 mode=IX associatedObjectId=1433553235
owner-list
owner id=process4c85288 mode=IX
waiter-list
waiter id=process4bd94c8 mode=X requestType=convert
我的理解如下:两个进程(让我们从它们的最后两个 id 字符中称它们为“c8”和“88”)设法在表上获得相同的 IX 锁,然后尝试变成 X 锁以删除必要的行,但一个进程阻塞了另一个进程。
我的理解正确吗?如果是,为什么进程共享 IX 锁?系统是否不应该拒绝访问 IX 锁来限制竞争条件的进程,这将导致第一个进程首先完成,然后第二个进程可以开始?
发表评论补充:在挖掘更多时,我发现where
条件上的索引不存在,而我希望它是聚集的主键。没有这个索引会不会是死锁的原因?
是的。每个查询都将从一个 IX 锁开始,然后开始获取键范围上的 U 锁。由于
holdlock
提示,它将使用范围锁定,否则它将是单个键上的常规 U 锁定。但是在使用 (updlock,holdlock) 扫描数千行之后,每个都会尝试将锁升级到表级 X 锁(对象锁是分区的)
两个会话都无法在表上获得 X 锁,因为另一个会话具有不兼容的 IX 锁,因此死锁。
使用选择性索引,每个会话只需要读取和锁定少数键值,因此不会升级为表锁定。