SQL Server 2008 R2 Dirty czyta - jak nieatomowy?

11

Zastanawiam się, jak „brudne” brudne odczyty mogą dostać się pod poziom izolacji niezamierzony odczytywania . Rozumiem, że wiersze, które zostały zaktualizowane, ale jeszcze nie zostały zatwierdzone, są widoczne, ale:

  1. Czy wiersz może być wyświetlany jako częściowo zaktualizowany - to znaczy niektóre kolumny są zaktualizowane, a niektóre nie?
  2. Czy pojedyncza kolumna może być częściowo zaktualizowana. Na przykład, jeśli masz kolumnę varchar (4000), która była w trakcie aktualizacji i zakładała, że ​​zawiera 4000 znaków. Czy potrafisz odczytać powiedzmy 2k znaków z poprzedniego stanu i 2k znaków z nowego stanu? Co z varcharem (maks.) O długości> 8k?

Aktualizacja: po krótkiej debacie minimalny konsensus jest taki, że jeśli rozmiar kolumny wynosi> 8 KB, możliwe są nieczytelne odczyty, nawet w samej kolumnie.

Michael Goldshteyn
źródło

Odpowiedzi:

7

EDYTOWANE po przeczytaniu linku do forum MSDN z komentarza , bardzo interesujące.

Niezależnie od poziomu izolacji, dwóch użytkowników nie może zaktualizować jednej strony jednocześnie, ani też żaden użytkownik nie może odczytać częściowo zaktualizowanej strony. Wyobraź sobie, jak SQL Server poradziłby sobie ze stroną, na której nagłówek mówi, że Col3 zaczyna się od bajtu 17. Ale tak naprawdę zaczyna się od bajtu 25, ponieważ ta część wiersza nie została jeszcze zaktualizowana. Baza danych nie może sobie z tym poradzić.

Jednak w przypadku wierszy większych niż 8 tys. Używanych jest wiele stron, dzięki czemu możliwa jest aktualizacja kolumny w połowie. Skopiowane z łącza MSDN (w przypadku zerwania łącza), rozpocznij zapytanie w jednym oknie:

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

Spowoduje to utworzenie tabeli, a następnie zaktualizuje ją ciągiem 100 000x tego samego znaku. Podczas działania pierwszego zapytania uruchom je w innym oknie:

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

Drugie zapytanie kończy się, gdy czyta kolumnę, która jest w połowie zaktualizowana. To znaczy, gdy pierwsza postać różni się od ostatniej. Skończy się szybko, udowadniając, że można odczytać kolumny częściowo zaktualizowane. Jeśli usuniesz nolockpodpowiedź, drugie zapytanie nigdy się nie zakończy.

Zaskakujący wynik! W połowie zaktualizowana kolumna XML może uszkodzić (nolock)raport, ponieważ kod XML byłby zniekształcony.

Andomar
źródło
1
Najwyraźniej nie jest to prawdą, według social.msdn.microsoft.com/Forums/en-US/transactsql/thread /... , ale typy kolumn, które można częściowo zaktualizować, nadal są tajemnicą.
@Andomar zatrzaskiwania AFAIK uniemożliwiałby odczyt częściowo zaktualizowanej strony, ale co by było, gdyby niektóre wartości kolumn zostały odczytane z NCI, ale wyszukano zakładkę, aby pobrać kolumnę z CI. Pod NOLOCKJestem pewien, że byłoby to możliwe, aby inżynier sytuację, w której kolumny NCI pochodziły z jednej wersji wiersza, ale CI z innej wersji. Dodatkowo dane poza wierszem i strony LOB nie byłyby chronione przez zatrzask na stronie danych.
Martin Smith
1
@Martin: Zgadzam się, widziałem, jak dołączyłem do siebie i nolocknie znalazłem oryginalnego wiersza. Jednak pojedynczy odczyt pola lub wiersza powinien być spójny.
Andomar
1
@Andomar, chyba że kolumny w wierszu obejmują wiele stron. Zobacz mój link.
2
To naprawdę powinno być CW, ponieważ oryginalna odpowiedź była nigdzie w pobliżu, Michael przedstawił sedno obecnej odpowiedzi. Twój komentarz do pytania wciąż nie zgadza się z edytowaną odpowiedzią.