2013-12-09 49 views
2

说我有一个简单的查询如使用WITH(NOLOCK)和交易

Select * From MyTable WITH (NOLOCK) 

而且,在此执行查询时的同时,另一个用户在插入100行到该表中的一个的上下文中交易。

在提交或回滚事务之前,Select语句是否理论上有可能因为它使用NOLOCK而读取插入表中的100行的子集?如果我正确理解NOLOCK,这似乎是可能的,但想要验证。

SQL Server 2012的

+0

其实我认为你可以在提交或回滚之前读取全部100个不只是一个子集。 – Paparazzi

+0

@Blam不同意。请看我更新的答案,否则证明。 –

+0

@AaronBertrand - 我想你可能是交叉目的。我将Blam的评论解释为*和读取受影响行的(适当的)子集一样,您还可以在提交/回滚之前读取整行行。 –

回答

10

当然,你可以阅读的子集或全部是事务的开始和提交或回滚之间影响了未提交的数据。这就是NOLOCK这一点 - 允许您读取未提交的数据,以便您不必等待作者,并避免放置大多数锁,以便作者不必等待。

证据#1

这是很容易证明的。在一个窗口中,创建该表:

CREATE TABLE dbo.what(id INT); 

在第二个窗口,运行此查询:

DECLARE @id INT; 

WHILE 1 = 1 
BEGIN 
SELECT @id = id FROM dbo.what WITH (NOLOCK) WHERE id = 2; 

IF @id = 2 
BEGIN 
    PRINT @id; 
    BREAK; 
END 
END 

现在回到第一个窗口,并开始有意长时间运行的事务,但卷回:

BEGIN TRANSACTION; 
GO 

INSERT dbo.what SELECT 2; 
GO 10000 

ROLLBACK TRANSACTION; 

只要你在第一个窗口开始这一声,在第二个窗口中的查询将暂停并会吐出被读取未提交的值。

证据#2

这主要是较量@布拉姆的评论上面,这点我不同意:

其实我觉得你可以阅读所有100不仅仅是一个前集到提交或回滚。

您当然可以读取受事务影响的行的子集。尝试以下类似示例,这次将100组数据插入表中1000次,并使用(NOLOCK)检索查询中的计数。窗口#1(上面,如果你还没有测试证明#1):

CREATE TABLE dbo.what(id INT); 

窗口#2:

DECLARE @c INT; 

WHILE 1 = 1 
BEGIN 
SELECT @c = COUNT(*) FROM dbo.what WITH (NOLOCK) WHERE id = 2; 

IF @c > 0 
    PRINT @c; 

IF @c > 10000 
    BREAK; 
END 

早在窗口#1:

BEGIN TRANSACTION; 
GO 

INSERT dbo.what SELECT TOP (100) 2 FROM sys.all_objects; 
GO 1000 

ROLLBACK TRANSACTION; 

窗口# 2将旋转,直到你开始交易。只要你这样做,你就会开始看到数字流入。但它们不会达到100的倍数(不要介意100,000,@Blam似乎正在做的全部或没有任何要求)。这里是我的删节结果:

1 
10 
12 
14 
17 
19 
23 
25 
29 
... 
85 
87 
91 
95 
98 
100 
100 
... 
9700 
9700 
9763 
9800 
9838 
9900 
9936 
10000 
10000 
10000 
10080 

NOLOCK查询显然不等待任何单个语句来读取数据之前完成,更不用说整个事务。因此,无论每个语句所影响的行数有多少,并且无论整个事务中有多少个语句,都可以使数据处于任何流动状态。

其他副作用

也有情况下NOLOCK可以skip rows, or read the same row twice,取决于扫描,并且当另一个事务正在生成页拆分的类型。基本上,当(NOLOCK)查询正在读取数据时,其他写入操作实际上可以将数据移动到其他位置 - 因为它们可以 - 将已读取的行移动到扫描中更远的位置,或者将行移动在您的扫描之前还没有读过。

咨询

在一般情况下,这是一个坏消息,你应该考虑READ_COMMITTED_SNAPSHOT代替 - 它让读者不会阻止作家和反之亦然相同的利益,但给你的一个一致的看法数据在某个时间点忽略所有后续的数据修改(尽管这对tempdb有影响,所以一定要测试它)。 Very thorough information here

3

正如亚伦已经很好地解释,是的,你会阅读肮脏的未提交的数据。但是,如果您想避免读取脏数据,并且尚未准备好开始使用乐观锁定,则可以尝试使用如下所示的READPAST表提示,尽管这样做的效果是它将跳过任何被锁定的行,所以您将看不到尚未提交的插入和更新的行。

SELECT * 
FROM MyTable WITH (READPAST) 

请注意,此表提示要求数据库中所承诺无论是读(默认),或是可重复读隔离级别下运行。