Dữ liệu vốn đã được sắp xếp như thể nó là một chỉ mục được nhóm


8

Tôi có bảng sau với 7,5 triệu hồ sơ:

CREATE TABLE [dbo].[TestTable](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [TestCol] [nvarchar](50) NOT NULL,
    [TestCol2] [nvarchar](50) NOT NULL,
    [TestCol3] [nvarchar](50) NOT NULL,
    [Anonymised] [tinyint] NOT NULL,
    [Date] [datetime] NOT NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Tôi nhận thấy rằng khi có một chỉ mục không được nhóm trên trường ngày:

CREATE NONCLUSTERED INDEX IX_TestTable_Date ON [dbo].[TestTable] ([Date])

-Và tôi chạy truy vấn sau:

UPDATE TestTable 
SET TestCol='*GDPR*', TestCol2='*GDPR*', TestCol3='*GDPR*', Anonymised=1
WHERE [Date] <= '25 August 2016'

-Các dữ liệu được trả về bởi hoạt động truy cập chỉ mục được sắp xếp để khớp với thứ tự chính của PK / CX, làm giảm hiệu suất.

Kế hoạch truy vấn

Tôi đã rất ngạc nhiên khi thấy rằng việc xóa chỉ mục khỏi trường ngày thực sự giúp cải thiện hiệu suất của truy vấn khoảng 30% vì nó không còn thực hiện sắp xếp:

Kế hoạch truy vấn

Lý thuyết của tôi, và điều này có thể rõ ràng với những người có kinh nghiệm hơn trong số các bạn, là nó đã chỉ ra rằng cột ngày được đặt hàng hoàn toàn giống với chỉ số khóa / cụm chính.

Vì vậy, câu hỏi của tôi là: Có thể tận dụng thực tế này để cải thiện hiệu suất của truy vấn của tôi?


1
Tôi đã không xem xét các kế hoạch nhưng tôi sẽ nghi ngờ hiệu suất (tốt, thời lượng, không có con số% chi phí ước tính vô dụng nào được cải thiện) vì nó không còn phải cập nhật chỉ mục bạn đã xóa, không phải do hoạt động sắp xếp.
Aaron Bertrand

@AaronBertrand Tôi có thể đang đọc những thứ này không chính xác, vì vậy vui lòng sửa cho tôi nếu tôi sai, nhưng dường như có một hoạt động cập nhật chỉ mục trong cả hai kế hoạch truy vấn. Bạn đang đề cập đến một cái gì đó khác?
AproposeArmadillo

1
Một lần nữa, tôi nói, tôi đã không nhìn vào các kế hoạch. Bạn đã nói "xóa chỉ mục khỏi trường ngày sẽ cải thiện hiệu năng của truy vấn" ... nếu bạn xóa chỉ mục, nó sẽ không xuất hiện trong kế hoạch, vì vậy có thể bạn đã thu thập kế hoạch sai hoặc không thực sự xóa chỉ số bạn nghĩ bạn đã làm. Và một lần nữa, một số% ước tính cho một kế hoạch là một chỉ số nhưng không thực sự phản ánh phép đo hiệu suất thực sự theo bất kỳ cách nào. Đó là một ước tính được tính trước khi truy vấn thậm chí chạy.
Aaron Bertrand

@Aaron Bertrand, dù sao cũng không phải cập nhật chỉ mục, vì [Ngày] không nằm trong số các trường được cập nhật.
Denis Rubashkin

1
@Shaffanhoon Bạn đã thử tạo lại chỉ mục trên [Date]nhưng theo DESCthứ tự? Chỉ tò mò từ vị ngữ là <=. Ngoài ra, nếu chỉ mục trên Date(theo mặc định, ACSthứ tự) giúp các truy vấn khác, thì có lẽ bạn có thể thử thêm một gợi ý bảng vào CẬP NHẬT để buộc nó sử dụng PK? Hoặc, có thể chia phần này thành hai phần: tạo bảng tạm thời, điền [Id]vào dựa trên [Date] <= '25 August 2016', sau đó xóa WHEREkhỏi CẬP NHẬT và thêm FROM dbo.TestTable tt INNER JOIN #tmp ids ON ids.[Id] = tt.[Id]. Rốt cuộc nó là một CẬP NHẬT, và nó cần tìm các hàng thực tế, chỉ mục hoặc không.
Solomon Rutzky

Câu trả lời:


7

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ả idgiá 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:

kế hoạch truy vấn ok

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 datecộ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 Anonymisedcột vào chỉ mục của mình, nhưng chỉ mục đó sẽ cần được cập nhật trong UPDATEtruy 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 Updatetoá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:

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

Đ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 Updatetoá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 Updatetoá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:

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

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.

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.