Trong SQL Server, làm thế nào để đọc khóa hoạt động?


17

Giả sử tôi có truy vấn chạy dài sau đây

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

và giả sử truy vấn sau được thực thi trong khi truy vấn trên đang chạy

SELECT *
FROM [Table1]

Có phải truy vấn đầu tiên ngăn truy vấn thứ hai chạy cho đến khi truy vấn đầu tiên được thực hiện? Nếu vậy, truy vấn đầu tiên có ngăn truy vấn thứ hai chạy trên tất cả các hàng hoặc chỉ các hàng liên quan đến mệnh đề WHERE không?

BIÊN TẬP:

Giả sử truy vấn thứ hai là

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements

Câu trả lời:


14

Tôi khuyên bạn nên đọc Hiểu cách SQL Server thực hiện một truy vấn , nó có giải thích về cách đọc và ghi hoạt động và cách khóa hoạt động.

Chế độ xem 10000ft như sau:

  • toán tử đọc thu được các khóa chia sẻ trên dữ liệu họ đọc, trước khi đọc dữ liệu
  • toán tử ghi có được các khóa độc quyền trên dữ liệu họ sửa đổi trước khi sửa đổi dữ liệu
  • khóa dữ liệu chỉ là chuỗi, ví dụ. một hàm băm của khóa được đọc trong phạm vi của cơ sở dữ liệu và đối tượng.
  • Trình quản lý khóa duy trì một danh sách tất cả các khóa được cấp và phát hiện sự không tương thích, theo ma trận Tương thích khóa
  • yêu cầu không tương thích bị đình chỉ cho đến khi cấp quyền không tương thích chặn chúng được phát hành
  • toán tử sử dụng hệ thống phân cấp khóa để khai báo ý định đọc hoặc cập nhật dữ liệu ở cấp cao hơn (cấp trang hoặc bảng, bỏ qua các tùy chọn cấp phân vùng). Điều này cho phép các nhà khai thác khóa toàn bộ các bảng mà không khóa mỗi hàng riêng lẻ
  • khóa đời và khóa phạm vi được sử dụng để thực thi các mức cô lập cao hơn

Đây thực sự chỉ là phần nổi của tảng băng. Chủ đề rất rộng lớn. Trong ví dụ của bạn, không ai có thể trả lời câu hỏi của bạn về những gì thực sự bị khóa bởi vì nó sẽ phụ thuộc vào nhiều yếu tố. Tất nhiên, không có ứng dụng nào phát hành SELECT * FROM Table1 vì nó thiếu một mệnh đề WHERE và đang sử dụng *. Đây là những thực tiễn xấu bởi vì, trong số những thứ khác, chúng sẽ dẫn đến sự tranh chấp chính xác.

Nếu bạn gặp khóa đọc so với ghi, bạn cần xem xét phiên bản hàng và cách ly ảnh chụp nhanh. Đọc Hiểu các mức cô lập dựa trên phiên bản hàng .


Điều gì xảy ra nếu tôi cần tất cả nội dung của một bảng (giả sử tôi chỉ có 14 hàng trong đó)? Làm thế nào là thực hành xấu SELECT * FROM Table1nếu đó là chính xác những gì tôi cần?
Azimuth

1
*tự nó là một thực tiễn xấu bởi vì khi cấu trúc bảng thay đổi, ứng dụng thường bị hỏng (kết quả là các cột không mong muốn xuất hiện).
Remus Rusanu

3

Chỉnh sửa: Như @MaxVernon chỉ ra, sau đây không phải là một gợi ý để sử dụng NOLOCK , và tôi rất nên đề cập đến việc thiết lập mức độ giao dịch READ UNCOMMITEDvà để ý nghĩa tiêu cực đứng ở vị trí đó hơn là đưa NOLOCKra ở vị trí đầu tiên. Vì vậy, như được đăng ban đầu:

Nhanh chóng và đơn giản là "Có, truy vấn đầu tiên sẽ chặn truy vấn thứ hai trừ khi một gợi ý chỉ mục cụ thể được chỉ định ( NOLOCK , đôi khi được gọi là" đọc bẩn ") hoặc mức cô lập giao dịch của truy vấn thứ hai được đặt thành READ UNCOMMITED(hoạt động giống hệt nhau), không nó không."

Để trả lời cho chi tiết bổ sung được cung cấp trong câu hỏi đòi hỏi phải bao gồm một WITHmệnh đề trên phần hai SELECT, loại trừ lẫn nhau hoặc nếu không, các tương tác giữa hai truy vấn sẽ phần lớn giống nhau.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

Trong một phiên riêng biệt, hãy chạy như sau:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Bạn có thể kiểm tra các khóa hiện đang được giữ bằng cách chạy sp_lock, tốt nhất là trong một phiên riêng biệt khác:

EXECUTE dbo.sp_lock;

Bạn sẽ thấy một KEYloại khóa được giữ bởi spid thực hiện giao dịch chèn trongX chế độ (độc quyền), không bị nhầm lẫn với các khóa IX(Intent-Exclusive) khác. Các khóa chứng chỉ ra rằng trong khi KEYkhóa là phạm vi cụ thể, nó cũng ngăn chặn các giao dịch khác từ chèn hoặc cập nhật các cột bị ảnh hưởng bởi thay đổi các dữ liệu chứa trong đó để nó có thể nằm trong phạm vi của truy vấn ban đầu. Vì bản thân khóa được giữ là độc quyền, truy vấn đầu tiên là ngăn truy cập vào tài nguyên từ bất kỳ giao dịch đồng thời nào khác. Trong thực tế, tất cả các hàng của cột bị khóa, cho dù chúng có nằm trong phạm vi được chỉ định bởi truy vấn đầu tiên hay không.

Các S khóa được giữ bởi phiên thứ hai sẽ WAITcho đến khi Xkhóa bị xóa, ngăn không cho khóa X(hoặc U) khác được lấy trên tài nguyên đó từ một spid đồng thời khác trước khi phiên thứ hai hoàn thành thao tác đọc của nó, chứng minh sự tồn tại của Skhóa.

Bây giờ là một chỉnh sửa cho rõ ràng: Trừ khi tôi nhầm lẫn về việc đọc bẩn là gì từ mô tả ngắn gọn về rủi ro được đề cập ở đây ... Chỉnh sửa 3 : Tôi chỉ nhận ra rằng tôi không xem xét ảnh hưởng của một điểm kiểm tra nền viết về giao dịch chưa được cam kết vào đĩa, vì vậy, giải thích của tôi là sai lệch.

Trong truy vấn thứ hai, lô đầu tiên có thể (và trong trường hợp này,) sẽ trả về dữ liệu không được cam kết. Lô thứ hai, chạy ở mức cô lập giao dịch mặc định READ COMMITEDsẽ chỉ trở lại sau khi hoàn thành cam kết hoặc khôi phục trong phiên đầu tiên.

Từ đây, bạn có thể xem các gói truy vấn của mình và các mức khóa liên quan, nhưng tốt hơn nữa, bạn có thể đọc tất cả về các khóa trong SQL Server tại đây .


1
Một lời cảnh báo về việc sử dụng WITH (NOLOCK)sẽ hữu ích trong trường hợp này. Xem brentozar.com/archive/2011/11/ trênbrentozar.com/archive/2013/02/ trên để đọc thêm.
Max Vernon

3
Ồ, WITH (NOLOCK)gợi ý không trả lại các trang bẩn từ bộ nhớ chưa được cam kết. Nó thực sự đọc các hàng từ bảng (cho dù trên đĩa hoặc được lưu trong bộ nhớ cache) mà không chặn các nhà văn cập nhật hoặc thêm các hàng vào các trang được sử dụng bởi bảng.
Max Vernon

2
Tôi bối rối. Nếu câu trả lời cho "truy vấn thứ 1 có ngăn thứ 2 chạy không?" là "Không", làm thế nào câu trả lời cho câu hỏi thứ hai là "Có"? Bạn có thể làm rõ câu hỏi nào bạn đang trả lời và mở rộng câu trả lời của bạn không?
Jon của tất cả các giao dịch

Chỉnh sửa galore, xin lỗi mọi người! Hãy cho tôi biết nếu có bất cứ điều gì khác không rõ ràng!
Avarkx
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.