对UPDLOCK,HOLDLOCK感到困惑


89

在研究表提示的使用时,我遇到了两个问题:

对这两个问题的回答都表明,使用时(UPDLOCK, HOLDLOCK),其他进程将无法读取该表上的数据,但是我没有看到这一点。为了进行测试,我创建了一个表并启动了两个SSMS窗口。在第一个窗口中,我运行了一个使用各种表提示从表中选择的事务。在事务运行时,我从第二个窗口运行了各种语句,以查看哪些将被阻止。

测试表:

CREATE TABLE [dbo].[Test](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Value] [nvarchar](50) NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

从SSMS窗口1:

BEGIN TRANSACTION

SELECT * FROM dbo.Test WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'

COMMIT TRANSACTION

从SSMS窗口2(运行以下操作之一):

SELECT * FROM dbo.Test
INSERT dbo.Test(Value) VALUES ('bar')
UPDATE dbo.Test SET Value = 'baz' WHERE Value = 'bar'
DELETE dbo.Test WHERE Value= 'baz'

不同的表提示对在Window 2中运行的语句的影响:

           (UPDLOCK)       (HOLDLOCK)    (UPDLOCK, HOLDLOCK)    (TABLOCKX)
---------------------------------------------------------------------------
SELECT    not blocked      not blocked       not blocked         blocked
INSERT    not blocked        blocked           blocked           blocked
UPDATE      blocked          blocked           blocked           blocked
DELETE      blocked          blocked           blocked           blocked

我是否误解了这些问题中给出的答案,或者在测试中犯了错误?如果没有,您为什么要单独使用(UPDLOCK, HOLDLOCK)vs (HOLDLOCK)


关于我要完成的工作的进一步说明:

我想从表中选择行,并防止在处理表时修改该表中的数据。我没有修改该数据,并希望允许进行读取。

这个答案清楚地表明,这(UPDLOCK, HOLDLOCK)将阻止读取(不是我想要的)。关于此答案的评论暗示,这是HOLDLOCK阻止阅读的原因。为了尝试更好地理解表提示的效果,看看是否有UPDLOCK单独的功能可以满足我的要求,我进行了上述实验,并得出了与那些答案相反的结果。

目前,我相信这(HOLDLOCK)是我应该使用的方式,但我担心自己可能犯了一个错误或忽略了某些东西,这些东西将来会再次咬住我,因此是这个问题。

Answers:


102

为什么会选择UPDLOCK块?该锁兼容性矩阵清楚地表明N了S / U和U / S争,在没有冲突

至于HOLDLOCK提示,文档指出:

HOLDLOCK:等效于SERIALIZABLE。有关更多信息,请参见本主题后面的SERIALIZABLE。

...

SERIALIZABLE:...扫描的语义与在SERIALIZABLE隔离级别运行的事务的语义相同。

事务隔离级别主题说明什么可序列化意味着:

在当前事务完成之前,没有其他事务可以修改当前事务已读取的数据。

在当前事务完成之前,其他事务不能插入键值将落入当前事务中任何语句读取的键范围内的新行。

因此,产品文档可以完美地说明您看到的行为:

  • UPDLOCK不会阻止并发SELECT或INSERT,但会阻止T1选择的行的任何UPDATE或DELETE
  • HOLDLOCK表示SERALIZABLE,因此允许SELECTS,但阻止T1选择的行以及T1选择的范围内的任何INSERT(这是整个表,因此是任何插入)的UPDATE和DELETES 。
  • (UPDLOCK,HOLDLOCK):您的实验未显示除上述情况外还会阻塞的内容,即在T2中使用UPDLOCK进行的另一笔交易
    SELECT * FROM dbo.Test WITH (UPDLOCK) WHERE ...
  • TABLOCKX无需解释

真正的问题是您要实现什么?玩锁提示而没有对锁语义的绝对完整的110%理解带来麻烦...

OP编辑后:

我想从表中选择行,并防止在处理表时修改该表中的数据。

您应使用较高的事务隔离级别之一。可重复读取将防止您读取的数据被修改。SERIALIZABLE将防止您读取的数据被修改以及新数据被插入。与使用查询提示相反,使用事务隔离级别是正确的方法。肯德拉·利特尔(Kendra Little)的海报很好地说明了隔离级别


+1,感谢您的详细回复。我将更新问题,以详细说明我的目标是什么。
杰夫·绪方

1
@Remus Rusanu您能否详细说明为什么正确的方法是使用隔离级别而不是使用查询提示?我有一个程序,我只需要锁定两个表以免被修改,而我正在使用TABLOCK,HOLDLOCK,我是否应该真正更改为隔离级别并在事务中锁定alls表?
史蒂夫,

我想解释一下TABLOCKX :)
niico

注意:Kendra Little博客条目的链接返回404。按照链接的提示,我找不到2011年2月2日的条目。
培根片,2013年

22

如果要在select语句中锁定一行或多行以用于将来的update语句,则使用UPDLOCK。将来的更新可能是事务中的下一个语句。

其他会话仍可以看到数据。他们只是无法获得与UPDLOCK和/或HOLDLOCK不兼容的锁。

当您要阻止其他会话更改已锁定的行时,请使用UPDLOCK。它限制了它们更新或删除锁定行的能力。

当您要阻止其他会话更改正在查看的任何数据时,可以使用HOLDLOCK。它限制了它们插入,更新或删除已锁定行的能力。这使您可以再次运行查询并看到相同的结果。


1
谢谢,但是我认为您并没有真正回答我的问题:这些问题的答案在说明该(UPDLOCK,HOLDLOCK)块读取时是否有误,是否有理由使用(UPDLOCK,HOLDLOCK)正义而不是正义(HOLDLOCK)
杰夫·绪方

我的第二句话回答了您的问题,它们是错误的。其他会话仍可以读取数据。
Scott Bruns,

上锁,保持锁与保持锁不同。Updlock,holdlock锁定行以进行更新并序列化您的事务。Holdlock本身只会序列化您的交易。它不会锁定选定的行以供进一步访问。
Scott Bruns

“当您想要在一条select语句中锁定一行或多行以用于将来的update语句时,使用UPDLOCK。” 我喜欢这个,因为XLOCK可能不被某个工作
逸品
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.