Máy chủ Sql - Chọn trong khi chèn vào giao dịch khác cho kết quả không mong muốn


8

Tôi vấp phải tình huống thay đổi kiến ​​thức về giao dịch và khóa cơ bản (mặc dù tôi không biết nhiều) và tôi cần giúp đỡ để hiểu nó.

Hãy nói rằng tôi có một bảng như thế này:

CREATE TABLE [dbo].[SomeTable](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[SomeData] [varchar](200) NOT NULL,
[Moment] [datetime] NOT NULL,
[SomeInt] [bigint] NOT NULL
) ON [PRIMARY]

và tôi chạy truy vấn "chèn 1000 hàng trong một giao dịch":

BEGIN TRAN t1

DECLARE @i INT = 0

WHILE @i < 1000
BEGIN
    SET @i = @i + 1

    INSERT INTO [SomeTable] ([SomeData] ,Moment, SomeInt)
    VALUES (CONVERT(VARCHAR(255), NEWID()), getdate(), @i)

    WAITFOR DELAY '00:00:00:010'
END

COMMIT TRAN t1

Trong khi giao dịch này đang chạy, tôi đang thực hiện một lựa chọn đơn giản:

SELECT Id, Moment, SomeData, SomeInt FROM [SomeTable]

Không phải lúc nào cũng có thể sao chép nó (rõ ràng phụ thuộc vào thời gian) nhưng đôi khi chọn truy vấn sẽ, sau khi chèn giao dịch kết thúc, trả về ít hơn 1000 hàng. Trong sự thiếu hiểu biết của tôi, tôi đã tin rằng select sẽ luôn trả về 1000 hàng (với mức độ cô lập là Read Cam kết), nhưng rõ ràng tôi đã hiểu sai về cách giao dịch và khóa hoạt động.

Tuy nhiên, nếu tôi đặt khóa chính trên cột Id (tạo chỉ mục được nhóm), hãy chọn truy vấn, miễn là tôi đã thử, trả về tất cả 1000 hàng. Đặt chỉ mục theo một cách khác, với chỉ mục được nhóm trên khóa tổng hợp và chỉ mục không được phân cụm trên một số cột khác, một lần nữa có thể dẫn đến việc trả lại số lượng hàng ít hơn tôi dự kiến.

Vì vậy, tôi có những câu hỏi sau:

  1. Tại sao chọn không luôn trả về tất cả các hàng được cam kết bởi giao dịch?
  2. Nếu đây là hành vi được mong đợi, cách tốt nhất để thực sự làm cho nó hoạt động như tôi mong đợi là gì? Về cơ bản, tôi muốn chọn trả về trạng thái của bảng sau (hoặc trước) giao dịch, chứ không phải một số dữ liệu được thực hiện một nửa. Ảnh chụp cách ly hiện không phải là một lựa chọn. Đặt TABLOCK dường như đang thực hiện công việc, nhưng có một giải pháp tốt hơn? Trong cuộc sống thực, tôi có các bảng mà tôi sẽ không muốn khóa ở cấp độ này nếu nó không thực sự cần thiết.
  3. Tại sao đặt một chỉ số thay đổi hành vi này?

Cảm ơn trước.

Câu trả lời:


12

Tôi đã không quản lý để tái tạo điều này sau khi chạy mã của bạn một vài lần.

Tôi cho rằng điều đó phải xảy ra khi một hàng sau được chèn vào một trang trước đó trong tệp.

Vì vậy, thứ tự của các hoạt động là (ví dụ)

  • Các hàng được chèn vào đống trên các trang 200, 207, 223
  • Chọn câu lệnh bắt đầu và thực hiện quét theo lệnh phân bổ. Tìm thấy rằng trang đầu tiên là 200 và bị chặn chờ đợi trên một khóa hàng sẽ được phát hành.
  • Các hàng khác được chèn bởi giao dịch đầu tiên. Một số trong số chúng được phân bổ trên một trang trước 200. Chèn cam kết giao dịch.
  • Khóa hàng được phát hành và tiếp tục phân bổ quét. Hàng trước đó trong tập tin bị bỏ lỡ.

Bảng bao gồm 10 trang. Theo mặc định, 8 trang đầu tiên sẽ được phân bổ từ các phạm vi hỗn hợp và sau đó nó sẽ được phân bổ một phạm vi thống nhất. Có thể trong không gian trường hợp của bạn đã có sẵn trong tệp cho một phạm vi thống nhất miễn phí trước các phạm vi hỗn hợp đã được sử dụng.

Bạn có thể kiểm tra lý thuyết này bằng cách chạy phần sau trong một cửa sổ khác sau khi bạn đã tái tạo vấn đề và xem liệu các hàng còn thiếu từ bản gốc có SELECTxuất hiện ở đầu tập kết quả này không.

SELECT [SomeData],
       Moment,
       SomeInt,
       file_id,
       page_id,
       slot_id
FROM   [SomeTable] 
/*Undocumented - Use at own risk*/
CROSS APPLY sys.fn_PhysLocCracker(%% physloc %%)
ORDER BY page_id, SomeInt

Hoạt động đối với bảng được lập chỉ mục sẽ theo thứ tự khóa chỉ mục thay vì thứ tự phân bổ, do đó sẽ không bị ảnh hưởng bởi kịch bản cụ thể này.

Việc quét theo lệnh phân bổ có thể được thực hiện đối với một chỉ mục nhưng nó chỉ được xem xét nếu bảng đủ lớn và mức cô lập được đọc không được cam kết hoặc khóa bảng được giữ.

Bởi vì đọc cam kết thường giải phóng các khóa ngay khi dữ liệu được đọc, có thể quét vào chỉ mục để đọc hàng hai lần hoặc không (nếu khóa chỉ mục được cập nhật bởi một giao dịch đồng thời làm cho hàng tiến lên hoặc lùi ) Xem Cấp độ cô lập đã đọc để thảo luận thêm về loại vấn đề này.


Theo cách ban đầu, tôi đã dự tính cho trường hợp được lập chỉ mục rằng chỉ mục nằm trên một trong các cột tăng tương đối so với thứ tự chèn (bất kỳ Id, Moment, someInt). Tuy nhiên ngay cả khi chỉ số cụm là ngẫu nhiênSomeData , vấn đề vẫn không phát sinh.

Tôi đã thử

DBCC TRACEON(3604, 1200, -1) /*Caution. Global trace flag. Outputs lock info
                               on every connection*/

SELECT TOP 2 *,
             %%LOCKRES%%
FROM   [SomeTable] WITH(nolock)
ORDER BY [SomeData];

SELECT *,
       %%LOCKRES%%
FROM   [SomeTable]
ORDER BY [SomeData];

/*Turn off trace flags. Doesn't check whether or not they were on already 
  before we started, with TRACEOFF*/
DBCC TRACEOFF(3604, 1200, -1)

Kết quả như sau

nhập mô tả hình ảnh ở đây

Kết quả thứ hai bao gồm tất cả 1.000 hàng. Thông tin khóa cho thấy rằng mặc dù nó đã bị chặn chờ trên tài nguyên khóa 24c910701749khi khóa được phát hành nhưng nó không chỉ tiếp tục quét từ thời điểm đó. Thay vào đó, nó ngay lập tức phát hành khóa đó và có được một khóa hàng trên hàng đầu tiên mới.

nhập mô tả hình ảnh ở đây


Martin cảm ơn vì đã nỗ lực và giải thích của bạn, nó rất hữu ích. Tôi sẽ mất một chút thời gian để thử nghiệm nhiều hơn với các ví dụ bạn cung cấp và các bảng thực tôi có. Cho đến nay, tôi đã tái tạo vấn đề (cách tiếp cận hit & miss, cũng thay đổi một chút thời gian chờ giao dịch và số lượng hàng được chèn trong một lần tới 10000), vì vậy hãy chọn thực hiện song song trả về 22684 hàng trong số 30000. Bằng cách truy vấn thử nghiệm bạn được cung cấp (fn_PhysLocCracker), các hàng bị loại trừ nằm trên các trang 384 đến 405, trong số 405. Đây là cách ba hàng trường hợp viền trông giống như: i62.tinypic.com/jrtngl.png
chọc

Điều thú vị là bảng thế giới thực có một CI trên bốn cột và 7 NCI, nhưng tôi vẫn có thể tái tạo vấn đề. Thiết kế bảng hoàn toàn xấu, nhưng tôi không thể làm quá nhiều ở bên đó (thiết kế kế thừa có rất nhiều truy vấn bên ngoài để chơi), ngoại trừ thay đổi các chỉ mục ngớ ngẩn đó. Tôi đã thử nghiệm bằng cách xóa tất cả chúng và chỉ để lại một CI trên cột nhận dạng và một NCI duy nhất, và vấn đề đã biến mất (rõ ràng). Tôi vẫn còn bối rối về việc thay đổi chỉ số có thể ảnh hưởng đến hành vi cơ bản như vậy. Tôi sẽ phải thử nghiệm nhiều hơn với các ví dụ bạn đã cung cấp để hiểu rõ hơn về nó.
chọc
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.