SQL Server 2008 R2 Dirty membaca - bagaimana non-atom?

11

Saya bertanya-tanya "seberapa kotor" bacaan kotor dapat berada di bawah tingkat isolasi baca-tidak terikat . Saya mengerti bahwa baris yang telah diperbarui tetapi belum dilakukan terlihat, tetapi:

  1. Dapatkah baris muncul sebagai sebagian diperbarui - yaitu, beberapa kolom diperbarui dan ada yang tidak?
  2. Dapatkah satu kolom muncul sebagian diperbarui. Misalnya, jika Anda memiliki kolom varchar (4000) yang sedang dalam proses diperbarui sepenuhnya dan anggap benar-benar berisi 4000 karakter. Bisakah Anda membaca katakanlah 2k chars dari kondisi sebelumnya dan 2k chars dari status barunya? Bagaimana dengan varchar (maks) dengan panjang> 8k?

Pembaruan: Setelah beberapa perdebatan, konsensus minimal adalah bahwa jika ukuran kolom> 8KB, pembacaan kotor, bahkan di dalam kolom itu sendiri, dimungkinkan.

Michael Goldshteyn
sumber

Jawaban:

7

Diedit setelah membaca tautan forum MSDN dari komentar , sangat menarik.

Terlepas dari tingkat isolasi, dua pengguna tidak dapat memperbarui satu halaman secara bersamaan, juga tidak ada pengguna yang dapat membaca halaman yang diperbarui sebagian. Bayangkan saja bagaimana SQL Server akan berurusan dengan halaman di mana header mengatakan Col3 dimulai pada byte 17. Tapi itu benar-benar dimulai pada byte 25, karena bagian dari baris itu belum diperbarui. Tidak mungkin database bisa mengatasinya.

Tetapi untuk baris yang lebih besar dari 8k, banyak halaman digunakan, dan itu memungkinkan kolom yang setengah diperbarui. Disalin dari tautan MSDN (jika tautan rusak), mulailah kueri ini dalam satu jendela:

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

Ini membuat tabel dan kemudian memperbaruinya dengan string 100.000x karakter yang sama. Saat kueri pertama berjalan, mulai kueri ini di jendela lain:

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

Kueri kedua berhenti ketika membaca kolom yang setengah diperbarui. Yaitu, ketika karakter pertama berbeda dari yang terakhir. Ini akan selesai dengan cepat, membuktikan bahwa mungkin untuk membaca kolom yang setengah diperbarui. Jika Anda menghapus nolockpetunjuk, permintaan kedua tidak akan pernah selesai.

Hasil yang mengejutkan! Kolom XML yang setengah diperbarui mungkin memecah (nolock)laporan, karena XML akan salah format.

Andomar
sumber
1
Ini tampaknya tidak selalu benar, per social.msdn.microsoft.com/Forums/en-US/transactsql/thread/… , tetapi jenis kolom apa yang dapat dilihat diperbarui sebagian masih agak menjadi misteri.
@Andomar kait AFAIK akan mencegah pembacaan halaman yang diperbarui sebagian tetapi bagaimana jika beberapa nilai kolom dibaca dari NCI tetapi melakukan pencarian bookmark untuk mengambil kolom dari CI. Di bawah NOLOCKSaya yakin akan mungkin untuk merekayasa situasi di mana kolom NCI berasal dari satu versi baris tetapi CI dari versi yang berbeda. Selain itu, data dari luar baris dan lob tidak akan dilindungi oleh kait pada halaman data.
Martin Smith
1
@ Martin: Setuju, saya telah melihat self join dengan nolocktidak menemukan baris aslinya. Namun, pembacaan tunggal bidang atau baris harus konsisten.
Andomar
1
@Andomar, kecuali kolom di baris menjangkau beberapa halaman. Lihat tautan saya.
2
Ini harus benar-benar CW karena jawaban aslinya tidak berada di dekat kanan, Michael memberikan inti dari jawaban saat ini. Komentar Anda terhadap pertanyaan masih tidak setuju dengan jawaban yang diedit.