Tại sao INSERT
câu lệnh thứ hai ~ 5x chậm hơn câu lệnh thứ nhất?
Từ số lượng dữ liệu nhật ký được tạo, tôi nghĩ rằng thứ hai không đủ điều kiện để ghi nhật ký tối thiểu. Tuy nhiên, tài liệu trong Hướng dẫn hiệu suất tải dữ liệu chỉ ra rằng cả hai phần chèn sẽ có thể được ghi lại tối thiểu. Vì vậy, nếu ghi nhật ký tối thiểu là sự khác biệt hiệu suất chính, tại sao truy vấn thứ hai không đủ điều kiện để ghi nhật ký tối thiểu? Có thể làm gì để cải thiện tình hình?
Truy vấn # 1: Chèn các hàng 5MM bằng CHERTN ... VỚI (TABLOCK)
Hãy xem xét các truy vấn sau, trong đó chèn các hàng 5MM thành một đống. Truy vấn này thực hiện trong 1 second
và tạo 64MB
dữ liệu nhật ký giao dịch như được báo cáo bởi sys.dm_tran_database_transactions
.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Truy vấn # 2: Chèn cùng một dữ liệu, nhưng SQL đánh giá thấp # của các hàng
Bây giờ hãy xem xét truy vấn rất giống nhau này, hoạt động trên cùng một dữ liệu chính xác nhưng tình cờ rút ra từ một bảng (hoặc SELECT
câu lệnh phức tạp có nhiều phép nối trong trường hợp sản xuất thực tế của tôi) trong đó ước tính cardinality quá thấp. Truy vấn này thực hiện trong 5.5 seconds
và tạo 461MB
dữ liệu nhật ký giao dịch.
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
Kịch bản đầy đủ
Xem Pastebin này để biết tập hợp đầy đủ các tập lệnh để tạo dữ liệu thử nghiệm và thực hiện một trong các kịch bản này. Lưu ý rằng bạn phải sử dụng cơ sở dữ liệu trong SIMPLE
mô hình khôi phục .
Bối cảnh kinh doanh
Chúng tôi thường xuyên di chuyển xung quanh hàng triệu hàng dữ liệu và điều quan trọng là các hoạt động này phải hiệu quả nhất có thể, cả về thời gian thực hiện và tải I / O của đĩa. Ban đầu, chúng tôi đã có ấn tượng rằng việc tạo một bảng heap và sử dụng INSERT...WITH (TABLOCK)
là một cách tốt để làm điều này, nhưng giờ đã trở nên kém tự tin hơn khi chúng tôi quan sát tình huống được trình bày ở trên trong một kịch bản sản xuất thực tế (mặc dù với các truy vấn phức tạp hơn, không phải là phiên bản đơn giản hóa tại đây).
SELECT
câu lệnh phức tạp với nhiều phép nối tạo ra tập kết quả choINSERT
. Các phép nối này tạo ra các ước tính cardinality kém cho toán tử chèn bảng cuối cùng (mà tôi đã mô phỏng trong tập lệnh repro thông quaUPDATE STATISTICS
cuộc gọi xấu ), và do đó không đơn giản như ban hànhUPDATE STATISTICS
lệnh để khắc phục sự cố. Tôi hoàn toàn đồng ý rằng việc đơn giản hóa truy vấn để Công cụ ước tính Cardinality dễ hiểu hơn có thể là một cách tiếp cận tốt, nhưng nó không phải là một trival để thực hiện logic kinh doanh phức tạp.