在研究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'
不同表提示对窗口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的评论意味着HOLDLOCK
阻止了读取。为了更好地理解表格提示的效果,看看单独UPDLOCK
是否能达到我想要的效果,我做了上述实验并得到了与这些答案相矛盾的结果。
目前,我认为(HOLDLOCK)
是我应该使用的,但我担心我可能犯了一个错误或忽略了将来会再次咬我的东西,因此这个问题。
答案 0 :(得分:89)
为什么UPDLOCK会选择? Lock Compatibility Matrix清楚地显示了{/ 1}}的S / U和U / S争用,如 No Conflict 。
至于HOLDLOCK提示文档说明:
HOLDLOCK:相当于SERIALIZABLE。有关更多信息,请参阅SERIALIZABLE 在本主题后面。
...
SERIALIZABLE:...扫描的执行语义与在SERIALIZABLE隔离级别运行的事务相同......
和Transaction Isolation Level主题解释了SERIALIZABLE的含义:
没有其他事务可以修改已被读取的数据 当前事务完成之前的当前事务。
其他事务无法插入具有键值的新行 落在当前任何语句读取的键范围内 交易直到当前交易完成。
因此,您看到的行为可以通过产品文档完美解释:
N
真正的问题是你想要实现的目标?玩锁定提示没有绝对完全110%的锁定语义理解乞求麻烦......
OP编辑后:
我想从表中选择行并阻止其中的数据 在我处理它时,表格被修改。
您应该使用较高的事务隔离级别之一。 REPEATABLE READ将阻止您读取的数据被修改。 SERIALIZABLE将阻止您读取的数据被修改和插入新数据。与使用查询提示相反,使用事务隔离级别是正确的方法。 Kendra Little有a nice poster exlaining the isolation levels。
答案 1 :(得分:19)
如果要在select语句期间为将来的更新语句锁定一行或多行,则使用UPDLOCK。未来的更新可能是交易中的下一个声明。
其他会话仍然可以看到数据。他们只是无法获得与UPDLOCK和/或HOLDLOCK不兼容的锁。
当您要阻止其他会话更改已锁定的行时,请使用UPDLOCK。它限制了他们更新或删除锁定行的能力。
如果要阻止其他会话更改您正在查看的任何数据,请使用HOLDLOCK。它限制了它们插入,更新或删除已锁定行的能力。这允许您再次运行查询并查看相同的结果。