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

Mic*_*eyn 11 sql-server-2008 sql-server sql-server-2008-r2

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

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

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

And*_*mar 7

从评论中阅读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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • 这真的应该是 CW,因为最初的答案远非正确,迈克尔提供了当前答案的要点。您对该问题的评论仍然不同意编辑后的答案。 (2认同)