Tôi đã chế nhạo dữ liệu thử nghiệm chủ yếu tái tạo vấn đề của bạn:
INSERT INTO [dbo].[TestTable] WITH (TABLOCK)
SELECT TOP (7000000) N'*NOT GDPR*', N'*NOT GDPR*', N'*NOT GDPR*', 0, DATEADD(DAY, q.RN / 16965, '20160801')
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
) q
ORDER BY q.RN
OPTION (MAXDOP 1);
DROP INDEX IF EXISTS [dbo].[TestTable].IX_TestTable_Date;
CREATE NONCLUSTERED INDEX IX_TestTable_Date ON [dbo].[TestTable] ([Date]);
Thống kê cho truy vấn sử dụng chỉ mục không bao gồm:
Bảng 'TestTable'. Quét số 1, đọc logic 1299838, đọc vật lý 0, đọc trước 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Thời gian thực thi máy chủ SQL: Thời gian CPU = 984 ms, thời gian trôi qua = 988 ms.
Thống kê cho truy vấn sử dụng chỉ mục được nhóm:
Bảng 'TestTable'. Quét số 1, đọc logic 72609, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Thời gian thực thi máy chủ SQL: Thời gian CPU = 781 ms, thời gian trôi qua = 772 ms.
Bắt đầu câu hỏi của bạn:
Có thể tận dụng thực tế này để cải thiện hiệu suất truy vấn của tôi không?
Đúng. Bạn có thể sử dụng chỉ mục không bao gồm mà bạn đã phải tìm một cách hiệu quả id
giá trị tối đa cần được cập nhật. Nếu bạn lưu nó vào một biến và lọc theo nó, bạn sẽ có một kế hoạch truy vấn cho bản cập nhật thực hiện quét chỉ mục cụm (không có sắp xếp) dừng sớm và do đó ít IO hơn. Đây là một triển khai:
DECLARE @Id INT;
SELECT TOP (1) @Id = Id
FROM dbo.TestTable
WHERE [Date] <= '25 August 2016'
ORDER BY [Date] DESC, Id DESC;
UPDATE TestTable
SET TestCol='*GDPR*', TestCol2='*GDPR*', TestCol3='*GDPR*', Anonymised=1
WHERE [Id] < @Id AND [Date] <= '25 August 2016'
AND [Anonymised] <> 1 -- optional
OPTION (MAXDOP 1);
Chạy số liệu thống kê cho truy vấn mới:
Bảng 'TestTable'. Quét số 1, đọc logic 3, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Bảng 'TestTable'. Quét số 1, đọc logic 4776, đọc vật lý 0, đọc trước đọc 0, đọc logic 0, đọc vật lý lob 0, đọc trước đọc 0, đọc trước 0.
Thời gian thực thi máy chủ SQL: Thời gian CPU = 515 ms, thời gian trôi qua = 510 ms.
Cũng như kế hoạch truy vấn:
Với tất cả những gì đã nói, mong muốn của bạn để làm cho truy vấn nhanh hơn gợi ý cho tôi rằng bạn có kế hoạch chạy truy vấn nhiều lần. Ngay bây giờ truy vấn của bạn có một bộ lọc kết thúc mở trên date
cột. Có thực sự cần thiết phải ẩn danh các hàng nhiều lần không? Bạn có thể tránh cập nhật hoặc quét các hàng đã được ẩn danh? Chắc chắn sẽ nhanh hơn để cập nhật một phạm vi ngày với ngày ở cả hai mặt của nó. Bạn cũng có thể thêm Anonymised
cột vào chỉ mục của mình, nhưng chỉ mục đó sẽ cần được cập nhật trong UPDATE
truy vấn của bạn . Tóm lại, tránh xử lý cùng một dữ liệu nhiều lần nếu bạn có thể.
Truy vấn ban đầu mà bạn có với sắp xếp chậm hơn do công việc được thực hiện trong Clustered Index Update
toán tử. Lượng thời gian dành cho tìm kiếm chỉ mục và sắp xếp chỉ là 407 ms. Bạn có thể thấy điều này trong kế hoạch thực tế. Kế hoạch thực hiện trong chế độ hàng, vì vậy thời gian dành cho sắp xếp là thời gian của toán tử đó cùng với mọi toán tử con:
Điều đó khiến toán tử sắp xếp với khoảng 1600 ms thời gian. SQL Server cần đọc các trang từ chỉ mục được nhóm để thực hiện cập nhật. Bạn có thể thấy rằng Clustered Index Update
toán tử thực hiện 1205921 lần đọc logic. Bạn có thể đọc thêm về cách sắp xếp tối ưu hóa cho DML và tìm nạp trước được tối ưu hóa trong bài đăng trên blog này của Paul White .
Gói truy vấn khác mà bạn có (không có sắp xếp) mất 683 ms cho quét chỉ mục cụm và khoảng 550 ms cho Clustered Index Update
toán tử. Toán tử cập nhật không thực hiện bất kỳ IO nào cho truy vấn này.
Câu trả lời đơn giản là tại sao kế hoạch với sắp xếp chậm hơn là SQL Server thực hiện nhiều lần đọc logic hơn trên chỉ mục được phân cụm cho kế hoạch đó so với kế hoạch quét chỉ mục được phân cụm. Ngay cả khi tất cả các dữ liệu cần thiết có trong bộ nhớ, vẫn có một chi phí và chi phí để thực hiện các lần đọc logic đó. Một câu trả lời tốt hơn là khó hơn nhiều để có được, theo như tôi biết các kế hoạch sẽ không cung cấp cho bạn bất kỳ chi tiết nào nữa. Có thể sử dụng PerfView hoặc một công cụ khác dựa trên theo dõi ETW để so sánh các ngăn xếp cuộc gọi giữa các truy vấn:
Bên trái là truy vấn thực hiện quét chỉ mục cụm và bên phải là truy vấn thực hiện sắp xếp. Tôi đã đánh dấu ngăn xếp cuộc gọi bằng màu xanh hoặc đỏ chỉ xuất hiện trong một truy vấn. Không có gì đáng ngạc nhiên, các ngăn xếp cuộc gọi khác nhau với số chu kỳ CPU được lấy mẫu cao cho truy vấn sắp xếp dường như phải thực hiện với các lần đọc logic cần thiết để thực hiện cập nhật trên chỉ mục được nhóm. Ngoài ra, có sự khác biệt về số lượng chu kỳ được lấy mẫu giữa các truy vấn cho cùng một hoạt động. Đối với mẫu, truy vấn với sắp xếp dành 31 chu kỳ để có được các chốt trong khi truy vấn với quá trình quét chỉ dành 9 chu kỳ để có được các chốt.
Tôi nghi ngờ rằng SQL Server đang chọn gói chậm hơn do giới hạn chi phí của toán tử kế hoạch truy vấn. Có lẽ một phần của sự khác biệt về thời gian chạy là do phần cứng hoặc phiên bản SQL Server của bạn. Trong mọi trường hợp, SQL Server không thể tìm ra rằng cột ngày được đặt hàng hoàn toàn giống với chỉ mục được nhóm. Dữ liệu được trả về từ quá trình quét chỉ mục được phân cụm theo thứ tự khóa được phân cụm, do đó không cần thực hiện sắp xếp để cố gắng tối ưu hóa IO khi thực hiện cập nhật chỉ mục theo cụm.