编辑:正如@MaxVernon所指出的,以下绝不是使用NOLOCK的建议,我非常应该刚才提到将事务级别设置为,READ UNCOMMITED
并让负面含义站在那里而不是首先提出NOLOCK
来。因此,如最初发布的:
快速简单的方法是:“是,除非指定了特定的索引提示(NOLOCK,有时称为“脏读”),或者第二个查询的事务隔离级别设置为READ UNCOMMITED
(相同地操作),否则第一个查询将阻止第二个查询,不,不是的。”
响应于问题中提供的其他细节,该细节WITH
要求在第二个元素上包含一个子句(SELECT
互斥或以其他方式互斥),因此两个查询之间的交互将基本相同。
IF NOT EXISTS ( SELECT 1
FROM sys.objects
WHERE name = 'Foo'
AND type = 'U' )
BEGIN
--DROP TABLE dbo.Foo;
CREATE TABLE dbo.Foo
(
Foo_PK BIGINT IDENTITY( 1, 1 ) NOT NULL,
PRIMARY KEY ( Foo_PK ),
Bar BIT,
x BIT,
y BIT,
z BIT
);
CREATE NONCLUSTERED INDEX IX_Foo_x
ON dbo.Foo ( x );
INSERT INTO dbo.Foo ( Bar, x, y, z )
VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;
GO
BEGIN TRANSACTION;
UPDATE dbo.Foo
SET y = 0
WHERE x = 1;
-- COMMIT TRANSACTION;
在一个单独的会话中,运行以下命令:
SELECT *
FROM dbo.Foo WITH ( NOLOCK );
GO
SELECT *
FROM dbo.Foo;
您可以通过运行来检查当前持有的锁sp_lock
,最好在另一个单独的会话中:
EXECUTE dbo.sp_lock;
您应该看到KEY
spid在X
(独占)模式下执行插入事务的spid持有一个类型锁,不要与其他IX
(Intent-Exclusive)锁混淆。所述锁定文档指示的同时KEY
锁是范围特异性的,它也可以防止其它事务插入或通过改变数据更新受影响的列包含在其中,以便它可以落入该范围内的原始查询的范围内。由于持有的锁本身是排他的,因此第一个查询将阻止任何其他并发事务访问资源。实际上,该列的所有行均被锁定,无论它们是否在第一个查询指定的范围内。
因此S
,第二个会话持有的锁将WAIT
一直保持到X
锁被清除为止,从而防止在第二个会话完成其读取操作之前,从另一个并发spid对该资源获取另一个X
(或U
)锁,从而证明该S
锁的存在。
现在为清楚起见进行编辑:除非我从这里提到的风险的简短描述中弄错了读物是什么,否则... 编辑3:我只是意识到我没有考虑写一个as的背景检查点的影响。尚未提交到磁盘的事务,所以是的,我的解释有误导性。
在第二个查询中,第一批可以(并且在这种情况下,将)返回未提交的数据。以默认事务隔离级别运行的第二批READ COMMITED
将仅在第一个会话中完成提交或回滚后返回。
从这里您可以查看查询计划和关联的锁级别,但是更好的是,您可以在此处阅读有关SQL Server中锁的所有信息。
SELECT * FROM Table1
如果这正是我所需要的,怎么做是不好的做法?