CHỌN LỰA CHỌN một hàng chậm hơn nhiều so với CHỌN riêng


18

Cho bảng heap sau với 400 hàng được đánh số từ 1 đến 400:

DROP TABLE IF EXISTS dbo.N;
GO
SELECT 
    SV.number
INTO dbo.N 
FROM master.dbo.spt_values AS SV
WHERE 
    SV.[type] = N'P'
    AND SV.number BETWEEN 1 AND 400;

và các cài đặt sau:

SET NOCOUNT ON;
SET STATISTICS IO, TIME OFF;
SET STATISTICS XML OFF;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Câu SELECTlệnh sau hoàn thành trong khoảng 6 giây ( bản demo , kế hoạch ):

DECLARE @n integer = 400;

SELECT
    c = COUNT_BIG(*) 
FROM dbo.N AS N
CROSS JOIN dbo.N AS N2
CROSS JOIN dbo.N AS N3
WHERE 
    N.number <= @n
    AND N2.number <= @n
    AND N3.number <= @n
OPTION
    (OPTIMIZE FOR (@n = 1));

Lưu ý: @ OPTIMIZE FORMệnh đề này chỉ nhằm mục đích tạo ra một repro có kích thước hợp lý, nắm bắt các chi tiết thiết yếu của vấn đề thực sự, bao gồm cả sự sai lệch về tim mạch có thể phát sinh vì nhiều lý do.

Khi đầu ra một hàng được ghi vào bảng, phải mất 19 giây ( bản demo , kế hoạch ):

DECLARE @T table (c bigint NOT NULL);

DECLARE @n integer = 400;

INSERT @T
    (c)
SELECT
    c = COUNT_BIG(*) 
FROM dbo.N AS N
CROSS JOIN dbo.N AS N2
CROSS JOIN dbo.N AS N3
WHERE 
    N.number <= @n
    AND N2.number <= @n
    AND N3.number <= @n
OPTION
    (OPTIMIZE FOR (@n = 1));

Các kế hoạch thực hiện xuất hiện giống hệt nhau ngoài việc chèn một hàng.

Tất cả thời gian thêm dường như được sử dụng bởi CPU.

Tại sao INSERTtuyên bố chậm hơn nhiều?

Câu trả lời:


21

SQL Server chọn quét các bảng heap ở phía bên trong của các vòng lặp tham gia bằng cách sử dụng các khóa cấp hàng. Quét toàn bộ thường chọn khóa cấp độ trang, nhưng sự kết hợp giữa kích thước của bảng và vị từ có nghĩa là công cụ lưu trữ chọn khóa hàng, vì đó dường như là chiến lược rẻ nhất.

Việc đánh giá sai về tim mạch được cố tình đưa ra bằng cách OPTIMIZE FORcó nghĩa là các đống được quét nhiều lần hơn trình tối ưu hóa mong đợi, và nó không đưa ra một ống chỉ như bình thường.

Sự kết hợp các yếu tố này có nghĩa là hiệu suất rất nhạy cảm với số lượng khóa cần thiết khi chạy.

Các SELECTlợi ích tuyên bố từ một tối ưu hóa cho phép khóa chia sẻ row-level để được bỏ qua (chỉ lấy ổ khóa trang cấp ý định chia sẻ) khi không có nguy cơ bị đọc dữ liệu không bị giam, và không có dữ liệu ngoài hàng.

Các INSERT...SELECTtuyên bố không được hưởng lợi từ tối ưu hóa này, vì vậy hàng triệu ổ khóa RID được thực hiện và phát hành mỗi giây trong trường hợp thứ hai, cùng với ổ khóa trang cấp ý định chia sẻ.

Số lượng lớn hoạt động khóa chiếm thêm CPU và thời gian trôi qua.

Cách giải quyết tự nhiên nhất là đảm bảo trình tối ưu hóa (và công cụ lưu trữ) có được ước tính số lượng thẻ tốt để họ có thể đưa ra lựa chọn tốt.

Nếu điều đó không thực tế trong trường hợp sử dụng thực tế, các câu lệnh INSERTSELECTcó thể được tách ra, với kết quả của việc SELECTgiữ trong một biến. Điều này sẽ cho phép SELECTtuyên bố được hưởng lợi từ tối ưu hóa bỏ qua khóa.

Thay đổi mức cô lập cũng có thể được thực hiện để hoạt động, bằng cách không sử dụng khóa chung hoặc bằng cách đảm bảo rằng việc leo thang khóa diễn ra nhanh chóng.

Là một điểm quan tâm cuối cùng, truy vấn có thể được thực hiện để chạy nhanh hơn cả SELECTtrường hợp được tối ưu hóa bằng cách buộc sử dụng các cuộn chỉ sử dụng cờ theo dõi không có giấy tờ 8691.

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.