我已经看到了许多与UPDLOCK(like this)结合使用的HOLDLOCK提示的示例。但是,针对这些提示的Microsoft's documentation使得HOLDLOCK似乎是多余的,因为UPDLOCK已经保持了锁定直到事务结束。 (也似乎说HOLDLOCK无论如何仅适用于共享锁。)

如果有的话,HOLDLOCK如何影响查询?

最佳答案

影响很大。

更新锁在行上具有更新锁,在页面上具有Intent更新,并在表/数据库上具有共享锁。

这不会阻止其他查询访问表中的数据,因为页面/数据库上的锁纯粹是共享锁。通过尝试执行与锁相反的操作,它们可能不会针对单个行/页/表冲突锁。如果发生这种情况,该请求将在当前锁后面排队,并等待它变为可用,然后才能继续。

通过使用holdlock,查询将被强制序列化,从而独占锁定表,直到操作完成。这样可以防止任何人读取表,除非使用了nolock提示,否则可能导致脏读。

要查看效果,请生成示例表'foo'并将一些垃圾数据放入其中。

begin tran

select * from foo with (updlock)
where tableid = 1
-- notice there is no commit tran

打开另一个窗口,然后尝试:
select * from foo

这些行又回来了,现在提交原始查询事务。重新运行更改为也使用holdlock:
begin tran

select * from foo with (updlock, holdlock)
where tableid = 1

返回到另一个窗口,然后再次尝试选择数据,由于该查询被排他锁阻止,因此该查询将不返回值。在第一个窗口上提交事务,由于不再被阻止,因此将出现第二个查询的结果。

最终测试是使用nolock,使用updlock和holdlock再次运行事务。然后在第二个窗口中运行以下命令:
select * from foo (nolock)

由于您已经接受了脏读(未提交读)的风险,因此结果将自动返回。

因此,它会产生很大的影响,因为您正在迫使针对该表的操作序列化,这可能是您想要的(取决于进行的更新),否则将在该表上创建很大的瓶颈。如果每个人都对运行了长时间事务的繁忙表执行此操作,则将导致应用程序内的严重延迟。

与所有SQL功能一样,正确使用它们可能会很强大,但是错误使用功能/提示可能会导致严重问题。我更喜欢将提示作为必须重写引擎的最后手段-而不是默认方法。

根据要求进行编辑:在SQL 2005、2008、2008R2(所有企业版)中经过测试-全部安装在几乎所有默认设置上,测试使用所有默认设置创建的数据库(仅输入数据库名称)。

09-26 09:48