SQL Server 2008 R2 Грязное чтение - насколько не атомарно?

11

Мне интересно, "как грязные" грязные чтения могут попасть под уровень изоляции без чтения . Я понимаю, что строки, которые были обновлены, но еще не зафиксированы, видны, но:

  1. Может ли строка выглядеть как частично обновленная, то есть некоторые столбцы обновлены, а некоторые нет?
  2. Может ли один столбец отображаться частично обновленным. Например, если у вас есть столбец varchar (4000), который полностью обновлялся и предполагал, что он на самом деле содержит 4000 символов. Можете ли вы прочитать, скажем, 2 КБ из предыдущего состояния и 2 КБ из его нового состояния? Как насчет varchar (max) длиной> 8 КБ?

Обновление: после некоторых дискуссий минимальный консенсус заключается в том, что, если размер столбца> 8 КБ, возможны «грязные» чтения, даже внутри самого столбца.

Михаил Гольдштейн
источник

Ответы:

7

Отредактировано после прочтения ссылки на форум MSDN из комментария , очень интересно.

Независимо от уровня изоляции, два пользователя не могут обновлять одну страницу одновременно, и никто не может прочитать частично обновленную страницу. Только представьте, как SQL Server будет обращаться со страницей, где заголовок говорит, что Col3 начинается с байта 17. Но он действительно начинается с байта 25, потому что эта часть строки еще не была обновлена. База данных не может справиться с этим.

Но для строк размером более 8 Кб используется несколько страниц, и это делает возможным половину обновленного столбца. Скопированный из ссылки 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 000 раз того же символа. Пока выполняется первый запрос, запустите этот запрос в другом окне:

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

Второй запрос останавливается, когда он читает столбец, который наполовину обновлен. То есть когда первый персонаж отличается от последнего. Это закончится быстро, доказав, что можно читать наполовину обновленные столбцы. Если вы удалите nolockподсказку, второй запрос никогда не закончится.

Удивительный результат! Наполовину обновленный столбец XML может привести к поломке (nolock)отчета, поскольку XML будет искажен.

Andomar
источник
1
По-видимому, это не всегда так, согласно social.msdn.microsoft.com/Forums/en-US/transactsql/thread/… , но какие типы столбцов можно увидеть частично обновленными, все еще остается загадкой.
Защелки @Andomar AFAIK будут препятствовать чтению частично обновленной страницы, но что, если некоторые значения столбцов будут считаны из NCI, но при этом будет выполнен поиск закладок для извлечения столбца из CI. В разделе « NOLOCKЯ уверен» можно было бы спроектировать ситуацию, когда столбцы NCI были из одной версии строки, а CI - из другой версии. Кроме того, данные вне строки и страницы лобов не будут защищены защелкой на странице данных.
Мартин Смит,
1
@Martin: Согласен, я видел, как я сам nolockне смог найти исходную строку. Тем не менее, одно чтение поля или строки должно быть последовательным.
Andomar
1
@Andomar, если столбцы в строке не занимают несколько страниц. Смотрите мою ссылку.
2
Это действительно должно быть CW, потому что оригинальный ответ был далеко не прав, Майкл предоставил суть текущего ответа. Ваш комментарий к вопросу по-прежнему не соответствует отредактированному ответу.