Nếu tôi hiểu chính xác yêu cầu, mục tiêu là xóa các lô hàng, đồng thời, các hoạt động DML đang diễn ra trên các hàng trong suốt bảng. Mục tiêu là xóa một lô; tuy nhiên, nếu bất kỳ hàng bên dưới nào trong phạm vi được xác định bởi lô đã nói bị khóa, thì chúng ta phải bỏ qua lô đó và chuyển sang lô tiếp theo. Sau đó chúng tôi phải quay lại bất kỳ lô nào chưa bị xóa trước đó và thử lại logic xóa ban đầu của chúng tôi. Chúng ta phải lặp lại chu trình này cho đến khi tất cả các lô hàng yêu cầu bị xóa.
Như đã đề cập, thật hợp lý khi sử dụng gợi ý READPAST và mức cô lập READ CAMITTED (mặc định), để bỏ qua các phạm vi có thể chứa các hàng bị chặn. Tôi sẽ tiến thêm một bước nữa và khuyên bạn nên sử dụng mức cô lập SERIALIZABLE và xóa các thao tác xóa.
SQL Server sử dụng các khóa Phạm vi khóa để bảo vệ một loạt các hàng được bao gồm trong bộ bản ghi đang được đọc bởi câu lệnh Transact-SQL trong khi sử dụng mức cô lập giao dịch tuần tự hóa ... tìm thêm tại đây:
https://technet.microsoft.com /en-US/l Library / ms191272 (v = Vista.105) .aspx
Với các thao tác xóa nibling, mục tiêu của chúng tôi là cô lập một phạm vi các hàng và đảm bảo rằng sẽ không có thay đổi nào xảy ra với các hàng đó trong khi chúng tôi đang xóa chúng, nghĩa là chúng tôi không muốn đọc hoặc chèn ảo. Mức cô lập tuần tự hóa có nghĩa là để giải quyết vấn đề này.
Trước khi tôi trình bày giải pháp của mình, tôi muốn nói thêm rằng tôi không khuyên bạn nên chuyển mức cô lập mặc định của cơ sở dữ liệu sang SERIALIZABLE cũng như tôi khuyên bạn nên giải pháp của mình là tốt nhất. Tôi chỉ muốn trình bày nó và xem chúng ta có thể đi đâu từ đây.
Một vài lưu ý giữ nhà:
- Phiên bản SQL Server mà tôi đang sử dụng là Microsoft SQL Server 2012 - 11.0.5343.0 (X64)
- Cơ sở dữ liệu thử nghiệm của tôi đang sử dụng mô hình phục hồi FULL
Để bắt đầu thử nghiệm, tôi sẽ thiết lập cơ sở dữ liệu thử nghiệm, bảng mẫu và tôi sẽ lấp đầy bảng với 2.000.000 hàng.
USE [master];
GO
SET NOCOUNT ON;
IF DATABASEPROPERTYEX (N'test', N'Version') > 0
BEGIN
ALTER DATABASE [test] SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
DROP DATABASE [test];
END
GO
-- Create the test database
CREATE DATABASE [test];
GO
-- Set the recovery model to FULL
ALTER DATABASE [test] SET RECOVERY FULL;
-- Create a FULL database backup
-- in order to ensure we are in fact using
-- the FULL recovery model
-- I pipe it to dev null for simplicity
BACKUP DATABASE [test]
TO DISK = N'nul';
GO
USE [test];
GO
-- Create our table
IF OBJECT_ID('dbo.tbl','U') IS NOT NULL
BEGIN
DROP TABLE dbo.tbl;
END;
CREATE TABLE dbo.tbl
(
c1 BIGINT IDENTITY (1,1) NOT NULL
, c2 INT NOT NULL
) ON [PRIMARY];
GO
-- Insert 2,000,000 rows
INSERT INTO dbo.tbl
SELECT TOP 2000
number
FROM
master..spt_values
ORDER BY
number
GO 1000
Tại thời điểm này, chúng ta sẽ cần một hoặc nhiều chỉ mục mà theo đó các cơ chế khóa của mức cô lập SERIALIZABLE có thể hoạt động.
-- Add a clustered index
CREATE UNIQUE CLUSTERED INDEX CIX_tbl_c1
ON dbo.tbl (c1);
GO
-- Add a non-clustered index
CREATE NONCLUSTERED INDEX IX_tbl_c2
ON dbo.tbl (c2);
GO
Bây giờ, chúng ta hãy kiểm tra xem 2.000.000 hàng của chúng tôi đã được tạo chưa
SELECT
COUNT(*)
FROM
tbl;
Vì vậy, chúng tôi có cơ sở dữ liệu, bảng, chỉ mục và hàng của chúng tôi. Vì vậy, chúng ta hãy thiết lập thử nghiệm để xóa các xóa. Đầu tiên, chúng ta phải quyết định cách tốt nhất để tạo ra một cơ chế xóa nibble điển hình.
DECLARE
@BatchSize INT = 100
, @LowestValue BIGINT = 20000
, @HighestValue BIGINT = 20010
, @DeletedRowsCount BIGINT = 0
, @RowCount BIGINT = 1;
SET NOCOUNT ON;
GO
WHILE @DeletedRowsCount < ( @HighestValue - @LowestValue )
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
DELETE
FROM
dbo.tbl
WHERE
c1 IN (
SELECT TOP (@BatchSize)
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN @LowestValue AND @HighestValue
ORDER BY
c1
);
SET @RowCount = ROWCOUNT_BIG();
COMMIT TRANSACTION;
SET @DeletedRowsCount += @RowCount;
WAITFOR DELAY '000:00:00.025';
CHECKPOINT;
END;
Như bạn có thể thấy, tôi đã đặt giao dịch rõ ràng bên trong vòng lặp while. Nếu bạn muốn hạn chế các bản ghi nhật ký, thì hãy đặt nó bên ngoài vòng lặp. Hơn nữa, vì chúng tôi đang ở trong mô hình phục hồi ĐẦY ĐỦ, bạn có thể muốn tạo bản sao lưu nhật ký giao dịch thường xuyên hơn trong khi chạy các hoạt động xóa nibble của bạn, để đảm bảo rằng nhật ký giao dịch của bạn có thể bị ngăn chặn phát triển quá mức.
Vì vậy, tôi có một vài mục tiêu với thiết lập này. Đầu tiên, tôi muốn khóa phạm vi khóa của tôi; Vì vậy, tôi cố gắng giữ các lô càng nhỏ càng tốt. Tôi cũng không muốn tác động tiêu cực đến sự tương tranh trên bảng "khổng lồ" của mình; Vì vậy, tôi muốn lấy ổ khóa của mình và để chúng nhanh nhất có thể. Vì vậy, tôi khuyên bạn nên làm cho kích thước lô của bạn nhỏ.
Bây giờ, tôi muốn cung cấp một ví dụ rất ngắn về thói quen xóa này trong hành động. Chúng tôi phải mở một cửa sổ mới trong SSMS và xóa một hàng khỏi bảng của chúng tôi. Tôi sẽ thực hiện việc này trong một giao dịch ngầm bằng cách sử dụng mức cô lập READ CAMITTED mặc định.
DELETE FROM
dbo.tbl
WHERE
c1 = 20005;
Hàng này đã thực sự bị xóa?
SELECT
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN 20000 AND 20010;
Vâng, nó đã bị xóa.
Bây giờ, để xem các khóa của chúng tôi, chúng ta hãy mở một cửa sổ mới trong SSMS và thêm một hoặc hai đoạn mã. Tôi đang sử dụng sp_whoisactive của Adam Mechanic, có thể tìm thấy ở đây: sp_whoisactive
SELECT
DB_NAME(resource_database_id) AS DatabaseName
, resource_type
, request_mode
FROM
sys.dm_tran_locks
WHERE
DB_NAME(resource_database_id) = 'test'
AND resource_type = 'KEY'
ORDER BY
request_mode;
-- Our insert
sp_lock 55;
-- Our deletions
sp_lock 52;
-- Our active sessions
sp_whoisactive;
Bây giờ, chúng tôi đã sẵn sàng để bắt đầu. Trong cửa sổ SSMS mới, chúng ta hãy bắt đầu một giao dịch rõ ràng sẽ cố gắng chèn lại một hàng mà chúng ta đã xóa. Đồng thời, chúng tôi sẽ thực hiện thao tác xóa nhanh.
Mã chèn:
BEGIN TRANSACTION
SET IDENTITY_INSERT dbo.tbl ON;
INSERT INTO dbo.tbl
( c1 , c2 )
VALUES
( 20005 , 1 );
SET IDENTITY_INSERT dbo.tbl OFF;
--COMMIT TRANSACTION;
Hãy để chúng tôi khởi động cả hai hoạt động bắt đầu với chèn và tiếp theo là xóa. Chúng ta có thể thấy các khóa phạm vi khóa và khóa độc quyền.
Việc chèn tạo ra các khóa này:
Xóa / chọn nibble đang giữ các khóa này:
Chèn của chúng tôi đang chặn xóa của chúng tôi như mong đợi:
Bây giờ, hãy để chúng tôi cam kết giao dịch chèn và xem những gì đang xảy ra.
Và như mong đợi, tất cả các giao dịch hoàn tất. Bây giờ, chúng ta phải kiểm tra xem liệu phần chèn có phải là một bóng ma hay không hay liệu thao tác xóa cũng xóa nó đi.
SELECT
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN 20000 AND 20015;
Trong thực tế, chèn đã bị xóa; vì vậy, không cho phép chèn ảo.
Vì vậy, để kết luận, tôi nghĩ rằng mục đích thực sự của bài tập này không phải là cố gắng theo dõi từng hàng, trang hoặc khóa cấp bảng và cố gắng xác định xem một phần tử của lô có bị khóa hay không và do đó sẽ yêu cầu thao tác xóa của chúng tôi chờ đợi. Đó có thể là ý định của người hỏi; tuy nhiên, nhiệm vụ đó là bất thường và về cơ bản là không thực tế nếu không muốn nói là không thể. Mục tiêu thực sự là đảm bảo rằng không có hiện tượng không mong muốn nào phát sinh khi chúng tôi đã tách phạm vi của lô của chúng tôi bằng các khóa của chính chúng tôi và sau đó trước khi xóa lô. Mức cô lập SERIALIZABLE đạt được mục tiêu này. Điều quan trọng là giữ cho số lượng nhỏ của bạn nhỏ, nhật ký giao dịch của bạn được kiểm soát và loại bỏ các hiện tượng không mong muốn.
Nếu bạn muốn tốc độ, thì đừng xây dựng các bảng sâu khổng lồ không thể phân vùng và do đó không thể sử dụng chuyển đổi phân vùng để có kết quả nhanh nhất. Chìa khóa của tốc độ là phân vùng và song song; chìa khóa của sự đau khổ là nhấm nháp và khóa trực tiếp.
Xin vui lòng cho tôi biết những gì bạn nghĩ.
Tôi đã tạo thêm một số ví dụ về mức độ cô lập SERIALIZABLE trong hành động. Họ nên có sẵn tại các liên kết dưới đây.
Xóa hoạt động
Chèn hoạt động
Hoạt động bình đẳng - Khóa phạm vi khóa trên các giá trị quan trọng tiếp theo
Hoạt động bình đẳng - Lấy dữ liệu đơn lẻ
Hoạt động bình đẳng - Lấy dữ liệu đơn lẻ
Hoạt động bất bình đẳng - Khóa phạm vi khóa trên phạm vi và các giá trị khóa tiếp theo