SQL Server 2008 R2 Bẩn đọc - không nguyên tử như thế nào?


11

Tôi đang tự hỏi "làm thế nào bẩn" đọc bẩn có thể nhận được dưới một mức cô lập không đọc được . Tôi hiểu rằng các hàng đã được cập nhật nhưng chưa được cam kết có thể nhìn thấy, nhưng:

  1. Một hàng có thể xuất hiện dưới dạng cập nhật một phần - nghĩa là, một số cột được cập nhật và một số thì không?
  2. Có thể một cột duy nhất xuất hiện cập nhật một phần. Ví dụ: nếu bạn có một cột varchar (4000) đang trong quá trình cập nhật hoàn toàn và giả sử nó thực sự chứa 4000 ký tự. Bạn có thể đọc nói 2k ký tự từ trạng thái trước và 2k ký tự từ trạng thái mới không? Điều gì về varchar (tối đa) với chiều dài> 8k?

Cập nhật: Sau một số tranh luận, sự đồng thuận tối thiểu là nếu kích thước cột> 8KB, việc đọc bẩn, ngay cả trong chính cột, là có thể.

Câu trả lời:


7

EDITED sau khi đọc liên kết diễn đàn MSDN từ bình luận , rất thú vị.

Bất kể mức độ cô lập, hai người dùng không thể cập nhật đồng thời một trang , và bất kỳ người dùng nào cũng không thể đọc một trang được cập nhật một phần. Chỉ cần tưởng tượng SQL Server sẽ xử lý một trang trong đó tiêu đề cho biết Col3 bắt đầu ở byte 17. Nhưng nó thực sự bắt đầu ở byte 25, bởi vì phần đó của hàng chưa được cập nhật. Không có cách nào một cơ sở dữ liệu có thể xử lý đó.

Nhưng đối với các hàng lớn hơn 8k, nhiều trang được sử dụng và điều đó làm cho một cột được cập nhật một nửa có thể. Sao chép từ liên kết MSDN (trong trường hợp liên kết bị hỏng), hãy bắt đầu truy vấn này trong một cửa sổ:

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

Điều này tạo ra một bảng và sau đó cập nhật nó với một chuỗi 100.000x cùng một ký tự. Trong khi truy vấn đầu tiên đang chạy, hãy bắt đầu truy vấn này trong một cửa sổ khác:

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

Truy vấn thứ hai dừng lại khi nó đọc một cột được cập nhật một nửa. Đó là, khi nhân vật đầu tiên khác với nhân vật cuối cùng. Nó sẽ kết thúc nhanh chóng, chứng minh rằng có thể đọc các cột được cập nhật một nửa. Nếu bạn xóa nolockgợi ý, truy vấn thứ hai sẽ không bao giờ kết thúc.

Kết quả đáng ngạc nhiên! Một cột XML được cập nhật một nửa có thể phá vỡ một (nolock)báo cáo, vì XML sẽ không đúng định dạng.


1
Điều này rõ ràng không phải lúc nào cũng đúng, theo social.msdn.microsoft.com/Forums/en-US/transactsql/thread/ , nhưng những loại cột có thể được cập nhật một phần vẫn còn là một bí ẩn.

@Andomar Các chốt AFAIK sẽ ngăn việc đọc trang được cập nhật một phần nhưng nếu một số giá trị cột được đọc từ NCI nhưng nó đã tìm kiếm dấu trang để lấy một cột từ CI. Theo NOLOCKtôi chắc chắn có thể thiết kế một tình huống trong đó các cột NCI từ một phiên bản của hàng nhưng CI từ một phiên bản khác. Ngoài ra, dữ liệu hàng và trang lob sẽ không được bảo vệ bằng chốt trên trang dữ liệu.
Martin Smith

1
@Martin: Đồng ý, tôi đã thấy một người tự tham gia mà nolockkhông tìm thấy hàng ban đầu của nó. Tuy nhiên, một lần đọc của một trường hoặc một hàng phải nhất quán.
Andomar

1
@Andomar, trừ khi các cột trong hàng trải dài nhiều trang. Xem liên kết của tôi.

2
Điều này thực sự nên là CW vì câu trả lời ban đầu không đúng, Michael đã cung cấp ý chính của câu trả lời hiện tại. Nhận xét của bạn chống lại câu hỏi vẫn không đồng ý với câu trả lời được chỉnh sửa.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.