Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Xem thông số, nhúng và các tùy chọn RECOMPILE

Parameter Sniffing

Tham số hóa truy vấn thúc đẩy việc sử dụng lại các kế hoạch thực thi đã lưu trong bộ nhớ cache, do đó tránh các biên dịch không cần thiết và giảm số lượng truy vấn đặc biệt trong bộ đệm kế hoạch.

Đây là tất cả những điều tốt, cung cấp truy vấn được tham số hóa thực sự phải sử dụng cùng một kế hoạch thực thi được lưu trong bộ nhớ cache cho các giá trị tham số khác nhau. Kế hoạch thực thi hiệu quả cho một giá trị tham số có thể không là một lựa chọn tốt cho các giá trị tham số có thể có khác.

Khi tính năng dò tìm tham số được bật (mặc định), SQL Server chọn một kế hoạch thực thi dựa trên các giá trị tham số cụ thể tồn tại tại thời điểm biên dịch. Giả định ngầm định là các câu lệnh được tham số hóa thường được thực thi nhất với các giá trị tham số chung nhất. Điều này nghe có vẻ hợp lý (thậm chí rõ ràng) và thực sự nó thường hoạt động tốt.

Sự cố có thể xảy ra khi bản dịch tự động của kế hoạch đã lưu trong bộ nhớ cache xảy ra. Việc biên dịch lại có thể được kích hoạt vì tất cả các loại lý do, chẳng hạn như vì một chỉ mục được sử dụng bởi kế hoạch đã lưu trong bộ nhớ cache đã bị loại bỏ ( tính đúng đắn biên dịch lại) hoặc do thông tin thống kê đã thay đổi (một tính tối ưu biên dịch lại).

nguyên nhân chính xác là gì về biên dịch lại kế hoạch, có khả năng là một không điển hình giá trị đang được chuyển dưới dạng tham số tại thời điểm tạo kế hoạch mới. Điều này có thể dẫn đến một kế hoạch được lưu trong bộ nhớ cache mới (dựa trên giá trị tham số không điển hình được đánh hơi) không tốt cho phần lớn các lần thực thi mà nó sẽ được sử dụng lại.

Không dễ dự đoán khi nào một kế hoạch thực thi cụ thể sẽ được biên dịch lại (ví dụ:vì số liệu thống kê đã thay đổi đủ) dẫn đến tình huống một kế hoạch có thể tái sử dụng chất lượng tốt có thể đột ngột bị thay thế bởi một kế hoạch khá khác được tối ưu hóa cho các giá trị tham số không điển hình.

Một trường hợp như vậy xảy ra khi giá trị không điển hình có tính chọn lọc cao, dẫn đến một kế hoạch được tối ưu hóa cho một số lượng nhỏ hàng. Các kế hoạch như vậy thường sẽ sử dụng thực thi đơn luồng, kết nối các vòng lặp lồng nhau và tra cứu. Các vấn đề nghiêm trọng về hiệu suất có thể phát sinh khi kế hoạch này được sử dụng lại cho các giá trị thông số khác nhau tạo ra số lượng hàng lớn hơn nhiều.

Disabling Parameter Sniffing

Tính năng dò tìm tham số có thể bị vô hiệu hóa bằng cách sử dụng cờ theo dõi được tài liệu hóa 4136. Cờ theo dõi cũng được hỗ trợ cho mỗi truy vấn sử dụng qua QUERYTRACEON gợi ý truy vấn. Cả hai đều áp dụng từ SQL Server 2005 Gói Dịch vụ 4 trở đi (và sớm hơn một chút nếu bạn áp dụng các bản cập nhật tích lũy cho Gói Dịch vụ 3).

Bắt đầu với SQL Server 2016, tính năng dò tìm tham số cũng có thể bị vô hiệu hóa ở cấp cơ sở dữ liệu , sử dụng PARAMETER_SNIFFING đối số cho ALTER DATABASE SCOPED CONFIGURATION .

Khi tính năng dò tìm tham số bị tắt, SQL Server sử dụng phân phối trung bình thống kê để chọn một kế hoạch thực hiện.

Điều này cũng có vẻ là một cách tiếp cận hợp lý (và có thể giúp tránh trường hợp trong đó kế hoạch được tối ưu hóa cho một giá trị tham số chọn lọc bất thường), nhưng nó cũng không phải là một chiến lược hoàn hảo:Một kế hoạch được tối ưu hóa cho giá trị 'trung bình' cũng có thể trở thành dưới mức tối ưu nghiêm trọng đối với các giá trị tham số thường thấy.

Hãy xem xét một kế hoạch thực thi có chứa các toán tử tiêu tốn bộ nhớ như sắp xếp và băm. Vì bộ nhớ được dự trữ trước khi bắt đầu thực thi truy vấn, một kế hoạch được tham số hóa dựa trên các giá trị phân phối trung bình có thể tràn sang tempdb cho các giá trị thông số chung tạo ra nhiều dữ liệu hơn mong đợi của trình tối ưu hóa.

Các dự trữ bộ nhớ thường không thể tăng lên trong quá trình thực thi truy vấn, bất kể máy chủ có thể có bao nhiêu bộ nhớ trống. Một số ứng dụng được hưởng lợi từ việc tắt tính năng dò tìm tham số (xem bài đăng lưu trữ này của Nhóm hiệu suất Dynamics AX để làm ví dụ).

Đối với hầu hết khối lượng công việc, vô hiệu hóa hoàn toàn tính năng dò tìm tham số là giải pháp sai , và thậm chí có thể là một thảm họa. Đánh giá tham số là một tối ưu hóa theo kinh nghiệm:Nó hoạt động tốt hơn so với việc sử dụng các giá trị trung bình trên hầu hết các hệ thống, hầu hết thời gian.

Gợi ý Truy vấn

SQL Server cung cấp một loạt các gợi ý truy vấn và các tùy chọn khác để điều chỉnh hành vi của việc dò tìm tham số:

  • OPTIMIZE FOR (@parameter = value) gợi ý truy vấn xây dựng một kế hoạch có thể tái sử dụng dựa trên một giá trị cụ thể.
  • OPTIMIZE FOR (@parameter UNKNOWN) sử dụng thống kê phân phối trung bình cho một thông số cụ thể.
  • OPTIMIZE FOR UNKNOWN sử dụng phân phối trung bình cho tất cả các tham số (hiệu ứng tương tự như cờ theo dõi 4136).
  • WITH RECOMPILE tùy chọn thủ tục được lưu trữ sẽ biên dịch một kế hoạch thủ tục mới cho mọi lần thực thi.
  • OPTION (RECOMPILE) gợi ý truy vấn biên soạn một kế hoạch mới cho một câu lệnh riêng lẻ.

Kỹ thuật cũ về “ẩn tham số” (gán các tham số thủ tục cho các biến cục bộ và tham chiếu các biến thay thế) có tác dụng tương tự như việc chỉ định OPTIMIZE FOR UNKNOWN . Nó có thể hữu ích trên các phiên bản trước SQL Server 2008 (OPTIMIZE FOR gợi ý là mới cho năm 2008).

Có thể lập luận rằng mọi câu lệnh được tham số hóa nên được kiểm tra độ nhạy đối với các giá trị tham số và được để riêng (nếu hành vi mặc định hoạt động tốt) hoặc được gợi ý rõ ràng bằng cách sử dụng một trong các tùy chọn ở trên.

Điều này hiếm khi được thực hiện trong thực tế, một phần vì thực hiện phân tích toàn diện cho tất cả các giá trị tham số có thể có có thể tốn thời gian và đòi hỏi các kỹ năng khá nâng cao.
Thông thường, không có phân tích nào như vậy được thực hiện và các vấn đề về độ nhạy tham số được giải quyết như và khi chúng xuất hiện trong quá trình sản xuất.

Việc thiếu phân tích trước này có lẽ là một trong những lý do chính khiến việc đánh hơi tham số có danh tiếng kém. Cần phải nhận thức được khả năng phát sinh các vấn đề và thực hiện ít nhất một phân tích nhanh về các câu lệnh có khả năng gây ra sự cố về hiệu suất khi được biên dịch lại với một giá trị tham số không điển hình.

Tham số là gì?

Một số người sẽ nói rằng một SELECT câu lệnh tham chiếu đến một biến cục bộ là một “câu lệnh được tham số hóa” trong số các loại, nhưng đó không phải là định nghĩa SQL Server sử dụng.

Bạn có thể tìm thấy dấu hiệu hợp lý rằng một câu lệnh sử dụng các tham số bằng cách xem xét các thuộc tính của kế hoạch (xem phần Tham số trong Sentry One Plan Explorer. Hoặc nhấp vào nút gốc của kế hoạch truy vấn trong SSMS, mở Thuộc tính và mở rộng Danh sách tham số nút):

‘Giá trị đã biên dịch’ hiển thị giá trị được đánh giá của tham số được sử dụng để biên dịch kế hoạch được lưu trong bộ nhớ cache. 'Giá trị thời gian chạy' hiển thị giá trị của tham số trên quá trình thực thi cụ thể được ghi lại trong kế hoạch.

Một trong hai thuộc tính này có thể bị trống hoặc bị thiếu trong các trường hợp khác nhau. Nếu một truy vấn không được tham số hóa, tất cả các thuộc tính sẽ đơn giản là bị thiếu.

Chỉ vì không có gì là đơn giản trong SQL Server, nên có những tình huống mà danh sách tham số có thể được điền, nhưng câu lệnh vẫn không được tham số hóa. Điều này có thể xảy ra khi SQL Server cố gắng tham số hóa đơn giản (sẽ thảo luận ở phần sau) nhưng lại quyết định rằng nỗ lực này là "không an toàn". Trong trường hợp đó, các dấu tham số sẽ hiện diện, nhưng kế hoạch thực thi trên thực tế không được tham số hóa.

Tính năng xem nhanh không chỉ dành cho các Thủ tục đã Lưu trữ

Đánh giá tham số cũng xảy ra khi một lô được tham số hóa rõ ràng để sử dụng lại bằng cách sử dụng sp_executesql .

Ví dụ:

EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'K%';

Trình tối ưu hóa chọn một kế hoạch thực thi dựa trên giá trị được đánh giá của @NameLike tham số. Giá trị thông số “K%” được ước tính khớp với rất ít hàng trong Product bảng, vì vậy trình tối ưu hóa chọn chiến lược tham gia vòng lặp lồng nhau và tìm kiếm khóa:

Việc thực thi lại câu lệnh với giá trị tham số là “[H-R]%” (giá trị này sẽ khớp với nhiều hàng hơn) sẽ sử dụng lại phương án tham số đã lưu trong bộ nhớ cache:

EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'[H-R]%';

AdventureWorks cơ sở dữ liệu mẫu quá nhỏ để khiến điều này trở thành thảm họa về hiệu suất, nhưng kế hoạch này chắc chắn không tối ưu cho giá trị tham số thứ hai.

Chúng tôi có thể thấy kế hoạch mà trình tối ưu hóa sẽ chọn bằng cách xóa bộ nhớ cache của kế hoạch và thực hiện lại truy vấn thứ hai:

Với số lượng kết hợp lớn hơn dự kiến, trình tối ưu hóa xác định rằng liên kết băm và tổng hợp băm là các chiến lược tốt hơn.

Hàm T-SQL

Tính năng dò tìm tham số cũng xảy ra với các hàm T-SQL, mặc dù cách các kế hoạch thực thi được tạo có thể khiến điều này khó thấy hơn.

Có những lý do chính đáng để tránh các hàm vô hướng và đa câu lệnh T-SQL nói chung, vì vậy chỉ dành cho mục đích giáo dục, đây là phiên bản hàm có giá trị bảng đa câu lệnh T-SQL của truy vấn thử nghiệm của chúng tôi:

CREATE FUNCTION dbo.F
    (@NameLike nvarchar(50))
RETURNS @Result TABLE
(
    ProductID   integer NOT NULL PRIMARY KEY,
    Name        nvarchar(50) NOT NULL,
    TotalQty    integer NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    INSERT @Result
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
 
    RETURN;
END;

Truy vấn sau sử dụng hàm để hiển thị thông tin cho tên sản phẩm bắt đầu bằng ‘K’:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Việc xem xét tham số với một hàm được nhúng sẽ khó hơn vì SQL Server không trả về một kế hoạch truy vấn sau thực thi (thực tế) riêng biệt cho từng lệnh gọi hàm. Hàm có thể được gọi nhiều lần trong một câu lệnh và người dùng sẽ không bị ấn tượng nếu SSMS cố gắng hiển thị một triệu kế hoạch gọi hàm cho một truy vấn.

Do quyết định thiết kế này, kế hoạch thực tế được SQL Server trả về cho truy vấn thử nghiệm của chúng tôi không hữu ích lắm:

Tuy nhiên, có các cách để xem đánh giá tham số đang hoạt động với các chức năng được nhúng. Phương pháp tôi đã chọn để sử dụng ở đây là kiểm tra bộ nhớ cache của gói:

SELECT
    DEQS.plan_generation_num,
    DEQS.execution_count,
    DEQS.last_logical_reads,
    DEQS.last_elapsed_time,
    DEQS.last_rows,
    DEQP.query_plan
FROM sys.dm_exec_query_stats AS DEQS
CROSS APPLY sys.dm_exec_sql_text(DEQS.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DEQS.plan_handle) AS DEQP
WHERE
    DEST.objectid = OBJECT_ID(N'dbo.F', N'TF');

Kết quả này cho thấy rằng kế hoạch chức năng đã được thực hiện một lần, với chi phí là 201 lần đọc logic với 2891 micro giây thời gian trôi qua và lần thực thi gần đây nhất trả về một hàng. Biểu diễn kế hoạch XML được trả về cho thấy rằng giá trị tham số was đánh hơi:

Bây giờ, hãy chạy lại câu lệnh, với một tham số khác:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'[H-R]%') AS Result;

Kế hoạch sau khi thực thi cho thấy rằng 306 hàng đã được trả về bởi hàm:

Truy vấn bộ đệm kế hoạch hiển thị kế hoạch thực thi được lưu trong bộ nhớ cache cho hàm đã được sử dụng lại (execution_count =2):

Nó cũng cho thấy số lần đọc logic cao hơn nhiều và thời gian trôi qua dài hơn so với lần chạy trước đó. Điều này phù hợp với việc sử dụng lại kế hoạch tra cứu và vòng lặp lồng nhau, nhưng để hoàn toàn chắc chắn, bạn có thể nắm bắt kế hoạch chức năng sau thực thi bằng cách sử dụng Sự kiện mở rộng hoặc SQL Server Profiler công cụ:

Bởi vì tính năng dò tìm tham số áp dụng cho các chức năng, các mô-đun này có thể phải chịu những thay đổi không mong muốn giống nhau về hiệu suất thường được liên kết với các thủ tục được lưu trữ.

Ví dụ:lần đầu tiên một chức năng được tham chiếu, một kế hoạch có thể được lưu trong bộ nhớ cache không sử dụng tính năng song song. Các lần thực thi tiếp theo với các giá trị tham số sẽ được hưởng lợi từ chế độ song song (nhưng sử dụng lại kế hoạch nối tiếp đã lưu trong bộ nhớ cache) sẽ cho thấy hiệu suất kém bất ngờ.

Vấn đề này có thể khó xác định vì SQL Server không trả về các kế hoạch sau thực thi riêng biệt cho các lệnh gọi hàm như chúng ta đã thấy. Sử dụng Sự kiện mở rộng hoặc Hồ sơ để nắm bắt thường xuyên các kế hoạch sau khi thực hiện có thể cực kỳ tốn nhiều tài nguyên, do đó, việc sử dụng kỹ thuật đó theo cách được nhắm mục tiêu thường rất hợp lý. Những khó khăn xung quanh việc gỡ lỗi các vấn đề về độ nhạy thông số của hàm có nghĩa là việc thực hiện phân tích (và mã hóa một cách bảo vệ) trước khi hàm được sản xuất thậm chí còn đáng giá hơn.

Tính năng dò tìm tham số hoạt động chính xác theo cùng một cách với các hàm vô hướng của T-SQL do người dùng xác định (trừ khi nằm trong hàng, trên SQL Server 2019 trở đi). Các hàm có giá trị bảng nội dòng không tạo ra một kế hoạch thực thi riêng biệt cho mỗi lệnh gọi, bởi vì (như tên gọi) chúng được xếp vào hàng trong truy vấn gọi trước khi biên dịch.

Hãy coi chừng những NULL bị đánh hơi

Xóa bộ nhớ cache của gói và yêu cầu một ước tính (trước khi thực hiện) kế hoạch cho truy vấn thử nghiệm:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Bạn sẽ thấy hai kế hoạch thực thi, kế hoạch thứ hai dành cho lệnh gọi hàm:

Một hạn chế của tính năng dò tìm tham số với các hàm được nhúng trong các kế hoạch ước tính có nghĩa là giá trị tham số được đánh giá là NULL (không phải “K%”):

Trong các phiên bản của SQL Server trước năm 2012, gói này (được tối ưu hóa cho NULL tham số) được lưu vào bộ nhớ đệm để sử dụng lại . Điều này thật không may, bởi vì NULL không có khả năng là một giá trị tham số đại diện và nó chắc chắn không phải là giá trị được chỉ định trong truy vấn.

SQL Server 2012 (và mới hơn) không lưu các kế hoạch vào bộ nhớ cache do yêu cầu "gói ước tính", mặc dù nó vẫn sẽ hiển thị một gói chức năng được tối ưu hóa cho một NULL giá trị tham số tại thời điểm biên dịch.

Đơn giản và Bắt buộc Tham số

SQL Server có thể tham số hóa một câu lệnh T-SQL đặc biệt chứa các giá trị chữ không đổi, bởi vì truy vấn đủ điều kiện cho tham số hóa đơn giản hoặc vì tùy chọn cơ sở dữ liệu cho tham số hóa bắt buộc được bật (hoặc một hướng dẫn kế hoạch được sử dụng để có tác dụng tương tự).

Một câu lệnh được tham số hóa theo cách này cũng phải tuân theo tham số. Truy vấn sau đây đủ điều kiện cho tham số hóa đơn giản:

SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Heidestieg Straße 8664';

Kế hoạch thực thi ước tính hiển thị ước tính 2,5 hàng dựa trên giá trị thông số được đánh giá:

Trên thực tế, truy vấn trả về 7 hàng (ước tính số lượng không hoàn hảo, ngay cả khi các giá trị được đánh giá):

Tại thời điểm này, bạn có thể tự hỏi đâu là bằng chứng cho thấy truy vấn này đã được tham số hóa và giá trị tham số kết quả đã tìm thấy. Chạy truy vấn lần thứ hai với một giá trị khác:

SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';

Truy vấn trả về một hàng:

Kế hoạch thực thi cho thấy lần thực thi thứ hai được sử dụng lại kế hoạch được tham số hóa đã được biên dịch bằng cách sử dụng một giá trị được đánh hơi:

Tham số hóa và đánh giá là các hoạt động riêng biệt

Một câu lệnh đặc biệt có thể được SQL Server tham số hóa mà không cần các giá trị tham số đang được đánh giá.

Để chứng minh, chúng tôi có thể sử dụng cờ theo dõi 4136 để vô hiệu hóa tính năng dò tìm tham số cho một lô sẽ được tham số hóa bởi máy chủ:

DBCC FREEPROCCACHE;
DBCC TRACEON (4136);
GO
SELECT
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE
    A.AddressLine1 = N'Heidestieg Straße 8664';
GO
SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';
GO
DBCC TRACEOFF (4136);

Tập lệnh dẫn đến các câu lệnh được tham số hóa, nhưng giá trị tham số không được đánh giá cho mục đích ước lượng bản số. Để xem điều này, chúng tôi có thể kiểm tra bộ nhớ cache của gói:

WITH XMLNAMESPACES
    (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
    DECP.cacheobjtype,
    DECP.objtype,
    DECP.usecounts,
    DECP.plan_handle,
    parameterized_plan_handle =
        DEQP.query_plan.value
        (
            '(//StmtSimple)[1]/@ParameterizedPlanHandle',
            'NVARCHAR(100)'
        )
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DECP.plan_handle) AS DEQP
WHERE 
    DEST.[text] LIKE N'%AddressLine1%'
    AND DEST.[text] NOT LIKE N'%XMLNAMESPACES%';

Kết quả hiển thị hai mục nhập trong bộ nhớ cache cho các truy vấn đặc biệt, được liên kết với kế hoạch truy vấn được tham số hóa (đã chuẩn bị) bởi bộ xử lý kế hoạch được tham số hóa.

Phương án tham số được sử dụng hai lần:

Kế hoạch thực thi hiển thị một ước tính bản số khác nhau hiện đã tắt tính năng dò tìm tham số:

So sánh ước tính của 1.44571 hàng với ước tính 2,5 hàng được sử dụng khi tính năng dò tìm thông số được bật.

Khi tắt tính năng đánh hơi, ước tính đến từ thông tin tần suất trung bình về AddressLine1 cột. Trích xuất của DBCC SHOW_STATISTICS đầu ra cho chỉ mục được đề cập cho biết số này được tính như thế nào:Nhân số hàng trong bảng (19,614) với mật độ (7,370826e-5) cho ra ước tính hàng là 1.44571.

Ghi chú bên lề: Người ta thường tin rằng chỉ các phép so sánh số nguyên sử dụng một chỉ mục duy nhất mới có thể đủ điều kiện cho tham số hóa đơn giản. Tôi đã cố tình chọn ví dụ này (so sánh chuỗi sử dụng chỉ mục không phải là duy nhất) để bác bỏ điều đó.

CÓ PHẢN HỒI và TÙY CHỌN (RECOMPILE)

Khi gặp vấn đề về độ nhạy thông số, một lời khuyên phổ biến trên các diễn đàn và trang Hỏi &Đáp là "sử dụng biên dịch lại" (giả sử các tùy chọn điều chỉnh khác được trình bày trước đó không phù hợp). Thật không may, lời khuyên đó thường bị hiểu sai có nghĩa là thêm WITH RECOMPILE tùy chọn cho thủ tục được lưu trữ.

Sử dụng WITH RECOMPILE đưa chúng ta trở lại hành vi SQL Server 2000 một cách hiệu quả, nơi toàn bộ quy trình được lưu trữ được biên dịch lại mỗi lần thực thi.

Một giải pháp thay thế tốt hơn , trên SQL Server 2005 trở lên, là sử dụng OPTION (RECOMPILE) gợi ý truy vấn chỉ về câu lệnh gặp phải vấn đề đánh hơi tham số. Gợi ý truy vấn này dẫn đến việc biên dịch lại câu lệnh có vấn đề chỉ còn. Các kế hoạch thực thi cho các câu lệnh khác trong thủ tục đã lưu trữ được lưu vào bộ nhớ đệm và sử dụng lại như bình thường.

Sử dụng WITH RECOMPILE cũng có nghĩa là kế hoạch đã biên dịch cho thủ tục được lưu trữ không được lưu vào bộ nhớ đệm. Do đó, không có thông tin hiệu suất nào được duy trì trong các DMV, chẳng hạn như sys.dm_exec_query_stats .

Thay vào đó, sử dụng gợi ý truy vấn có nghĩa là một kế hoạch đã biên dịch có thể được lưu vào bộ nhớ đệm và thông tin hiệu suất có sẵn trong DMV (mặc dù nó bị giới hạn ở lần thực thi gần đây nhất, chỉ dành cho câu lệnh bị ảnh hưởng).

Đối với các trường hợp chạy ít nhất SQL Server 2008 bản dựng 2746 (Gói dịch vụ 1 với Bản cập nhật tích lũy 5), sử dụng OPTION (RECOMPILE) có một lợi thế đáng kể khác over WITH RECOMPILE :Chỉ OPTION (RECOMPILE) bật Tối ưu hóa nhúng tham số .

Tối ưu hóa Nhúng Tham số

Đánh giá các giá trị tham số cho phép trình tối ưu hóa sử dụng giá trị tham số để lấy các ước tính về số lượng. Cả WITH RECOMPILEOPTION (RECOMPILE) dẫn đến các kế hoạch truy vấn với các ước tính được tính toán từ các giá trị tham số thực tế trên mỗi lần thực thi.

Tối ưu hóa nhúng tham số đưa quá trình này tiến thêm một bước. Tham số truy vấn được thay thế với các giá trị không đổi theo nghĩa đen trong quá trình phân tích cú pháp truy vấn.

Trình phân tích cú pháp có khả năng đơn giản hóa phức tạp một cách đáng ngạc nhiên và việc tối ưu hóa truy vấn tiếp theo có thể tinh chỉnh mọi thứ hơn nữa. Hãy xem xét quy trình được lưu trữ sau, có tính năng WITH RECOMPILE tùy chọn:

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
WITH RECOMPILE
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC;
END;

Thủ tục được thực thi hai lần, với các giá trị tham số sau:

EXECUTE dbo.P
	@NameLike = N'K%',
	@Sort = 1;
GO
EXECUTE dbo.P
	@NameLike = N'[H-R]%',
	@Sort = 4;

Bởi vì WITH RECOMPILE được sử dụng, thủ tục được biên dịch lại đầy đủ trên mỗi lần thực thi. Các giá trị tham số được đánh hơi mỗi lần và được trình tối ưu hóa sử dụng để tính toán các ước tính về bản số.

Kế hoạch thực hiện thủ tục đầu tiên là chính xác, ước tính 1 hàng:

Lần thực thi thứ hai ước tính 360 hàng, rất gần với 366 hàng được thấy trong thời gian chạy:

Cả hai kế hoạch đều sử dụng cùng một chiến lược thực thi chung:Quét tất cả các hàng trong một chỉ mục, áp dụng WHERE vị ngữ mệnh đề làm dư; tính toán CASE biểu thức được sử dụng trong ORDER BY mệnh đề; và thực hiện Sắp xếp N hàng đầu trên kết quả của CASE biểu thức.

TÙY CHỌN (RECOMPILE)

Bây giờ, hãy tạo lại quy trình đã lưu trữ bằng OPTION (RECOMPILE) gợi ý truy vấn thay vì WITH RECOMPILE :

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Việc thực thi quy trình được lưu trữ hai lần với cùng các giá trị tham số như trước sẽ tạo ra khác biệt đáng kể kế hoạch thực hiện.

Đây là kế hoạch thực thi đầu tiên (với các tham số yêu cầu tên bắt đầu bằng “K”, được sắp xếp theo ProductID tăng dần):

Trình phân tích cú pháp nhúng các giá trị tham số trong văn bản truy vấn, dẫn đến dạng trung gian sau:

SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    'K%' IS NULL
    OR Name LIKE 'K%'
ORDER BY
    CASE WHEN 1 = 1 THEN ProductID ELSE NULL END ASC,
    CASE WHEN 1 = 2 THEN ProductID ELSE NULL END DESC,
    CASE WHEN 1 = 3 THEN Name ELSE NULL END ASC,
    CASE WHEN 1 = 4 THEN Name ELSE NULL END DESC;

Sau đó, trình phân tích cú pháp đi xa hơn, loại bỏ các mâu thuẫn và đánh giá đầy đủ CASE biểu thức. Điều này dẫn đến:

SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    Name LIKE 'K%'
ORDER BY
    ProductID ASC,
    NULL DESC,
    NULL ASC,
    NULL DESC;

Bạn sẽ nhận được thông báo lỗi nếu bạn cố gắng gửi truy vấn đó trực tiếp đến SQL Server, bởi vì việc sắp xếp theo một giá trị không đổi là không được phép. Tuy nhiên, đây là biểu mẫu do trình phân tích cú pháp tạo ra. Nó được cho phép nội bộ vì nó phát sinh do áp dụng tối ưu hóa nhúng tham số . Truy vấn được đơn giản hóa làm cho cuộc sống của trình tối ưu hóa truy vấn trở nên dễ dàng hơn rất nhiều:

Quét chỉ mục theo cụm áp dụng LIKE vị ngữ làm dư. Tính toán vô hướng cung cấp hằng số NULL các giá trị. Trên cùng trả về 5 hàng đầu tiên theo thứ tự được cung cấp bởi Chỉ mục theo cụm (tránh một sự sắp xếp). Trong một thế giới hoàn hảo, trình tối ưu hóa truy vấn cũng sẽ xóa Tính vô hướng xác định NULLs , vì chúng không được sử dụng trong quá trình thực thi truy vấn.

Lần thực thi thứ hai diễn ra chính xác theo cùng một quy trình, dẫn đến một kế hoạch truy vấn (đối với các tên bắt đầu bằng các chữ cái “H” đến “R”, được sắp xếp theo Name giảm dần) như thế này:

Kế hoạch này có tính năng Tìm kiếm chỉ mục không phân biệt bao gồm LIKE phạm vi, một LIKE còn lại vị từ, hằng số NULLs như trước, và một Top (5). Trình tối ưu hóa truy vấn chọn thực hiện BACKWARD quét phạm vi trong Tìm kiếm chỉ mục để tránh sắp xếp một lần nữa.

So sánh kế hoạch ở trên với kế hoạch được tạo bằng WITH RECOMPILE , không thể sử dụng tối ưu hóa nhúng tham số :

Ví dụ demo này có thể đã được triển khai tốt hơn dưới dạng một chuỗi IF các câu lệnh trong thủ tục (một cho mỗi tổ hợp các giá trị tham số). Điều này có thể cung cấp các lợi ích kế hoạch truy vấn tương tự, mà không phải biên dịch câu lệnh mỗi lần. Trong các tình huống phức tạp hơn, biên dịch lại cấp câu lệnh với nhúng tham số được cung cấp bởi OPTION (RECOMPILE) có thể là một kỹ thuật tối ưu hóa cực kỳ hữu ích.

Hạn chế về nhúng

Có một trường hợp khi sử dụng OPTION (RECOMPILE) sẽ không dẫn đến việc áp dụng tối ưu hóa nhúng tham số. Nếu câu lệnh gán cho một biến, các giá trị tham số không được nhúng:

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    DECLARE
        @ProductID integer,
        @Name nvarchar(50);
 
    SELECT TOP (1)
        @ProductID = ProductID,
        @Name = Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Bởi vì SELECT câu lệnh bây giờ được gán cho một biến, các kế hoạch truy vấn được tạo ra giống như khi WITH RECOMPILE đã được dùng. Các giá trị tham số vẫn được trình tối ưu hóa truy vấn đánh giá và sử dụng để ước tính bản số và OPTION (RECOMPILE) vẫn chỉ biên dịch một câu lệnh duy nhất, chỉ lợi ích của nhúng tham số bị mất.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm việc xung quanh các tối ưu hóa bị bỏ lỡ

  2. Giới thiệu về Thư viện SQL Python

  3. Phù hợp với mô hình:Thú vị hơn khi tôi còn là một đứa trẻ

  4. Tham số Sniffing Primer

  5. Biểu thức bảng thông thường:Khi nào và làm thế nào để sử dụng chúng