带UPDLOCK的HOLDLOCK

时间:2013-01-10 18:16:27

标签: sql-server

在事务中显示使用HOLDLOCKUPDLOCK(例如T1),不会阻止来自另一个事务的读取访问(比如T2)。

据我了解,在T1完成之前,HOLDLOCK将阻止T2更新/删除;并且UPDLOCK将阻止T2更新/删除/插入。在这两个T2中都有对这些记录的读访问权。

但是,使用两者(例如:HOLDLOCK, UPDLOCK)即使是读取访问也会阻止T2。当我们同时使用它们时究竟会发生什么?

感谢您的见解

更新

这不是我所看到的:

例如:

在查询1中:

begin tran

select * from tblTest WITH (UPDLOCK, HOLDLOCK)

WAITFOR DELAY '00:00:10'

commit tran

在查询2中:

select * from tblTest 

在查询1完成之前,查询2不会产生结果。

3 个答案:

答案 0 :(得分:11)

UPDLOCK会影响锁定类型。这意味着SELECT语句将采用U锁而不是S锁。在默认的读取提交级别,它们将在读取数据后立即释放。

以上内容适用于行和页锁。对于表级锁定BOL状态

  

如果UPDLOCK与TABLOCK结合使用,或者采用表级锁定   由于某些其他原因,将采用独占(X)锁定。

HOLDLOCK表示您获得了可序列化的隔离语义,因此在事务结束之前锁定不会被释放,并且至少会锁定查询所涵盖的整个范围以防止插入幻像。

U锁与其他S锁兼容,但不与其他U锁兼容(请参阅Lock compatibility matrix),因此如果锁在行或页面级别取出除非他们也使用UPDLOCK提示,否则不会阻止其他读者。

如果由于X导致对象级UPDLOCK锁定被取消,则会阻止读者 尝试获取表格上的IS锁定。在您的示例查询中,请尝试查看sys.dm_tran_locks,同时阻止第二个查询以查看两个事务已等待/正在等待的锁定。

对于您问题中的查询

SELECT *
FROM   tblTest WITH (UPDLOCK, HOLDLOCK) 

如果查询计划在堆上显示扫描,您将始终在对象上获得X锁定。如果是索引扫描,则取决于所使用的锁定粒度(通常在执行at least 5,000次级锁定后尝试锁定升级到表级别)。

答案 1 :(得分:4)

我相信马丁已经解释了updlock如何导致排他锁定(+1)......我宁愿将其发布为评论/问题,但我的评论太大了...... / p>

以下是导致updlock锁定的x的简单示例...

IF (OBJECT_ID('tblTest') IS NOT NULL)
    DROP TABLE tblTest

CREATE TABLE tblTest (
    ID INT NOT NULL
)

BEGIN TRANSACTION
    SELECT * FROM dbo.tblTest WITH (UPDLOCK, HOLDLOCK) WHERE ID = 1
    SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
COMMIT

但是,如果向表中添加聚簇索引,则独占表锁将消失,并替换为RangeS-U锁...

ALTER TABLE dbo.tblTest 
ADD CONSTRAINT PK_tblTest 
PRIMARY KEY CLUSTERED (ID)

BEGIN TRANSACTION
    SELECT * FROM dbo.tblTest WITH (UPDLOCK, HOLDLOCK) WHERE ID = 1
    SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
COMMIT

所以基本上,你在这张桌子上有聚集索引吗?

编辑:

使用非聚集索引的另一个例子......

IF (OBJECT_ID('tblTest') IS NOT NULL)
    DROP TABLE tblTest

CREATE TABLE tblTest (
    ID INT NOT NULL
)

CREATE NONCLUSTERED INDEX 
IX_tblTest ON dbo.tblTest (ID) 

BEGIN TRANSACTION
    SELECT * FROM dbo.tblTest WITH (HOLDLOCK) WHERE ID = 1
    SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
COMMIT

会导致RangeS-S锁定...

但是...

BEGIN TRANSACTION
    SELECT * FROM dbo.tblTest WITH (UPDLOCK, HOLDLOCK) WHERE ID = 1
    SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID 
COMMIT

会导致独占表锁...

答案 2 :(得分:1)

如果表没有适当的查询索引,则可序列化隔离会导致执行表锁定。 HOLDLOCK使serializable成为上面提到的表的有效事务隔离级别。

这与其他人提到的X升级一起导致您看到的行为。