HOLDLOCK对UPDLOCK有什么影响?

mar*_*jne 30 t-sql sql-server locking

我已经看到很多HOLDLOCK提示与UPDLOCK结合使用的例子(像这样).但是Microsoft的这些提示的文档使得看起来HOLDLOCK应该是多余的,因为UPDLOCK已经持续锁定直到事务结束.(似乎也说HOLDLOCK只适用于共享锁.)

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

And*_*rew 63

它有很大的影响.

Update锁对行进行Update锁定,对页面进行Intent更新,对表/数据库进行共享锁定.

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

通过使用holdlock,强制查询序列化,独占锁定表直到操作完成.这可以防止任何人读取表,除非使用了nolock提示,允许读取可能脏的内容.

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

begin tran

select * from foo with (updlock)
where tableid = 1
-- notice there is no commit tran
Run Code Online (Sandbox Code Playgroud)

打开另一个窗口并尝试:

select * from foo
Run Code Online (Sandbox Code Playgroud)

行返回,现在提交原始查询事务.重新运行它也改为使用holdlock:

begin tran

select * from foo with (updlock, holdlock)
where tableid = 1
Run Code Online (Sandbox Code Playgroud)

返回到另一个窗口并尝试再次选择数据,查询将不会返回值,因为它被独占锁阻止.在第一个窗口上提交事务,将显示第二个查询的结果,因为它不再被阻止.

最后的测试是使用nolock,使用updlock和holdlock再次运行事务.然后在第二个窗口中运行以下命令:

select * from foo (nolock)
Run Code Online (Sandbox Code Playgroud)

结果将自动返回,因为您已接受脏读的风险(未提交读取).

因此,它被认为会产生很大的影响,因为您强制对该表进行序列化操作,这可能是您想要的(取决于所做的更新),或者会在该表上创建一个非常大的瓶颈.如果每个人都使用长时间运行的事务处理繁忙的表,那么它将导致应用程序中的重大延迟.

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

编辑为请求:在SQL 2005,2008,2008R2(所有企业版)中测试 - 全部安装在几乎默认的设置上,使用所有默认值创建的测试数据库(仅输入数据库的名称).

  • 你非常正确的NOLOCK不是默认值,但是请在没有NOLOCK的情况下测试你上面的代码......无论是否在第一个窗口中添加了HOLDLOCK,你都会看到SELECT执行. (2认同)

Dar*_*ren 13

安德鲁的答案是正确的,根据MSDN文档,但我测试对2008R2和2012年我没有看到这种行为所以请自己测试

我看到的行为如下:

首先在Play数据库上运行它.

CREATE TABLE [dbo].[foo](
    [tableid] [int] IDENTITY(1,1) NOT NULL,
    [Col2] [varchar](100) NOT NULL,
    CONSTRAINT [PK_foo] PRIMARY KEY CLUSTERED 
    (
        [tableid] ASC
    )
)
Run Code Online (Sandbox Code Playgroud)

......然后放几排.

现在将此代码粘贴到两个查询选项卡中(更改选项卡二中的"选项卡一"文本):

begin tran

select * from foo with (UPDLOCK, HOLDLOCK)
where tableid = 1

UPDATE foo SET Col2 = 'tab one'
where tableid = 1

commit tran
Run Code Online (Sandbox Code Playgroud)

把它放在另一个标签3中:

select * from foo
where tableid = 1
Run Code Online (Sandbox Code Playgroud)
  1. 确保指向表所在的播放数据库.

  2. 突出显示选项卡1中的更新语句之前所有内容并执行.

  3. 选项卡2中执行相同操作,您会发现选项卡2将无法完成并仍在执行.

  4. 现在在我完成的环境中执行选项卡3 的简单SELECT .

  5. 突出显示选项卡1中的更新语句并执行它(不要执行提交),您将看到选项卡2仍处于执行状态.

  6. 继续执行选项卡1中的提交... 选项卡2现在将完成选择...您可以运行其余的.

  • 另一种可能性,您开启了基于行版本控制的隔离——当基于行版本控制的隔离到位时,“简单”选择不会发出共享锁,只是一个 Sch-S 锁——这将防止它被阻塞,尽管隔离级别不是 2008 年的默认值。找出测试中的差异会很有趣。 (2认同)
  • 对于我来说,这在2008R2中的表现与@Darren描述的相同; 如果有待处理的事务,则第二个选择总是成功,如果它不使用任何提示. (2认同)
  • 它是一个旧帖子,但我认为添加它仍然是公平的。我在 2008 R2 的全新安装中得到了与 Darren 相同的结果 (2认同)