SQL Server 2008 R2脏读-如何非原子性?


11

我想知道在未提交未提交的隔离级别下,“脏”读取会“多么脏” 。我知道已经更新但尚未提交的行是可见的,但是:

  1. 行是否可以显示为部分更新-即某些列已更新而另一些未更新?
  2. 单个列是否可以显示为部分更新。例如,如果您有一个varchar(4000)列正在完全更新并且假设它实际上包含4000个字符。您能读说以前状态的2k字符和新状态的2k字符吗?长度大于8k的varchar(max)呢?

更新:经过一番辩论,最小的共识是,如果列大小> 8KB,则即使在列本身内也可能进行脏读。

Answers:


7

EDITED看完后从MSDN评论论坛的链接,非常有趣。

无论隔离级别如何,两个用户都不能同时更新单个页面,任何用户也不能读取部分更新的页面。试想一下,SQL Server将如何处理标题为Col3从字节17开始的页面,但实际上是从字节25开始,因为该行的那部分尚未更新。数据库无法处理该问题。

但是对于大于8k的行,则使用多个页面,这使得半更新列成为可能。从MSDN链接复制(以防链接断开),请在一个窗口中启动此查询:

if object_id('TestTable') is not null
    drop table TestTable
create table TestTable (txt nvarchar(max) not null)
go
insert into TestTable select replicate(convert(varchar(max),
    char(65+abs(checksum(newid()))%26)),100000)
go 10
update TestTable set txt=replicate(convert(varchar(max),
    char(65+abs(checksum(newid()))%26)),100000)
go 100000

这将创建一个表,然后使用100.000x相同字符的字符串对其进行更新。在第一个查询运行时,在另一个窗口中启动此查询:

while 1=1 begin
 if exists (select * from TestTable (nolock) where left(Txt,1) <> right(Txt,1))
    break
end

第二个查询在读取更新一半的列时停止。也就是说,当第一个字符与最后一个字符不同时。它将很快完成,证明可以读取半更新的列。如果删除nolock提示,第二个查询将永远不会完成。

令人惊讶的结果!半更新的XML列可能会中断(nolock)报告,因为XML格式不正确。


1
根据social.msdn.microsoft.com/Forums/en-US/transactsql/thread/…,这显然并不总是正确的,但是可以看到部分更新的列类型仍然是一个谜。

@Andomar AFAIK锁存器将阻止读取部分更新的页面,但是如果从NCI读取了一些列值,但是却做了书签查找以从CI检索列,该怎么办。在NOLOCK我的肯定下,可以设计一种情况,其中NCI列来自该行的一个版本,而CI来自不同版本。另外,行外数据和lob页将不受数据页上的锁存器的保护。
马丁·史密斯

1
@马丁:同意,我看到了一个自我加入,nolock但找不到原始行。但是,一次读取字段或行应该保持一致。
2011年

1
@Andomar,除非行中的列跨越多页。看到我的链接。

2
这真的应该是CW,因为原始答案远非正确,Michael提供了当前答案的要点。您对问题的评论仍然与编辑后的答案不一致。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.