8

它似乎在一个事务(比如 T1)中使用HOLDLOCK或使用UPDLOCK,不会阻止来自另一个事务(比如 T2)的读取访问。

据我了解,在 T1 完成之前,HOLDLOCK将阻止 T2 更新/删除;并且 aUPDLOCK将阻止 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 不会产生结果。

4

3 回答 3

11

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

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

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

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

U锁与其他锁兼容,但与其他锁S不兼容U(请参阅锁兼容性矩阵),因此如果在行或页面级别取出锁,这不会阻塞其他读取器,除非他们也使用UPDLOCK提示。

但是,如果对象级X锁被取出,UPDLOCK那么读取器被阻止尝试获取IS表上的锁。在您的示例查询中,尝试查看sys.dm_tran_locks第二个查询被阻止的情况,以查看两个事务具有/正在等待的锁。

对于您问题中的查询

SELECT *
FROM   tblTest WITH (UPDLOCK, HOLDLOCK) 

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

于 2013-01-10T18:20:59.453 回答
4

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

这是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

会导致排他表锁...

于 2013-01-10T18:49:57.047 回答
1

如果表没有适合查询的索引,可序列化隔离会导致表锁定。HOLDLOCK 使可序列化成为提及它的表的有效事务隔离级别。

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

于 2013-01-10T18:51:13.533 回答