【问题标题】:Confused about UPDLOCK, HOLDLOCK对UPDLOCK,HOLDLOCK感到困惑
【发布时间】:2011-10-21 00:12:53
【问题描述】:

在研究Table Hints的使用时,我遇到了这两个问题:

两个问题的答案都说当使用(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) 而不是(HOLDLOCK)


进一步解释我要完成的工作:

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

This answer 明确表示(UPDLOCK, HOLDLOCK) 将阻止读取(不是我想要的)。 this answer 上的 cmets 暗示是 HOLDLOCK 阻止读取。为了尝试更好地理解表格提示的效果并查看单独 UPDLOCK 是否可以满足我的要求,我进行了上述实验并得到了与这些答案相矛盾的结果。

目前,我认为(HOLDLOCK) 是我应该使用的,但我担心我可能犯了错误或忽略了将来会回来咬我的东西,因此提出了这个问题。

【问题讨论】:

    标签: sql-server tsql sql-server-2008 concurrency locking


    【解决方案1】:

    为什么 UPDLOCK 会阻止选择? Lock Compatibility Matrix 清楚地显示了 S/U 和 U/S 争用的 N,如 无冲突

    至于HOLDLOCK 提示文档指出:

    HOLDLOCK:相当于SERIALIZABLE。有关详细信息,请参阅可序列化 稍后在本主题中。

    ...

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

    Transaction Isolation Level 主题解释了 SERIALIZABLE 的含义:

    没有其他事务可以修改已被 当前事务直到当前事务完成。

    其他事务不能插入带有键值的新行 落在当前任何语句读取的键范围内 交易直到当前交易完成。

    因此,产品文档完美地解释了您看到的行为:

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

    真正的问题是你想要达到什么目的?在没有完全 110% 理解锁定语义的情况下使用锁定提示是在自找麻烦……

    OP 编辑​​后:

    我想从表中选择行并阻止其中的数据 表在我处理时不会被修改。

    您应该使用较高的事务隔离级别之一。 REPEATABLE READ 将防止您读取的数据被修改。 SERIALIZABLE 将防止您读取的数据被修改新数据被插入。与使用查询提示相反,使用事务隔离级别是正确的方法。 Kendra Little 有a nice poster exlaining the isolation levels

    【讨论】:

    • +1,感谢您的详细回复。我将更新我的问题以添加我的目标的详细信息。
    • @Remus Rusanu 您能否详细说明为什么正确的方法是使用隔离级别而不是使用查询提示?我有一个过程,我只需要锁定两个表不被修改,并且我正在使用 TABLOCK、HOLDLOCK,我真的应该更改为隔离级别并锁定我的事务中的所有表吗?
    • 我想解释一下 TABLOCKX :)
    • 注意:Kendra Little 的博客条目链接返回 404。如链接所示,我找不到日期为 2011 年 2 月 2 日的条目。
    【解决方案2】:

    UPDLOCK 用于在选择语句期间锁定一行或多行以供将来的更新语句使用。未来的更新可能是事务中的下一条语句。

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

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

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

    【讨论】:

    • 谢谢,但我认为您并没有真正回答我的问题:这些问题的答案是否错误地指出 (UPDLOCK,HOLDLOCK) 块读取,并且有理由使用 @987654322 @ 而不仅仅是(HOLDLOCK)?
    • 我的第二个陈述回答了你的问题,他们错了。其他会话仍然可以读取数据。
    • Updlock,Holdlock 和holdlock 不一样。 Updlock,holdlock 锁定行以进行更新并序列化您的事务。 Holdlock 本身只是序列化您的交易。它不会锁定选定的行以供进一步访问。
    • "当您想在 select 语句中为将来的更新语句锁定一行或多行时,使用 UPDLOCK。"我喜欢这个,因为 XLOCK 有时可能无法正常工作
    猜你喜欢
    • 1970-01-01
    • 2019-09-13
    • 2012-07-22
    • 2013-05-13
    • 2020-04-16
    • 2023-03-08
    • 2019-08-04
    • 2019-12-30
    • 2022-01-20
    相关资源
    最近更新 更多