Mỗi đợt gây ra một biên dịch


10

Chúng tôi có một ứng dụng bên thứ ba gửi các câu lệnh T-SQL theo lô.

Cơ sở dữ liệu được lưu trữ trên SQL Server 2016 Enterprise SP1 CU7, 16 lõi và bộ nhớ 256GB. Tối ưu hóa cho Ad-Hoc được bật.

Đây là một ví dụ giả về các truy vấn đang được thực hiện:

exec sp_executesql N'
IF @@TRANCOUNT = 0 SET TRANSACTION ISOLATION LEVEL SNAPSHOT

select field1, field2 from table1 where field1=@1
option(keep plan, keepfixed, loop join)

select field3, field4 from table2 where field3=@1
option(keep plan, keepfixed, loop join)', N'@1 nvarchar(6)',@1=N'test'

Khi tôi giám sát cơ sở dữ liệu và tôi xem các đợt / giây và biên dịch / giây, tôi nhận thấy chúng luôn giống nhau. Dưới tải nặng, đây có thể là 1000 lô / giây và 1000 biên dịch / giây. Dưới tải trung bình, có 150 lô / giây.

Tôi phân tích bộ đệm truy vấn cho các kế hoạch được biên dịch gần đây:

SELECT TOP (1000) qs.creation_time
    , DatabaseName = DB_NAME(st.dbid)
    , qs.execution_count
    , st.text
    , qs.plan_handle
    , qs.sql_handle
    , qs.query_hash 
FROM sys.dm_exec_query_stats qs
    CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) AS st
ORDER BY creation_time DESC;

Khi tôi chạy truy vấn trên, tôi chỉ thấy 10-20 kế hoạch truy vấn mới / giây.

Giống như mọi sp_executesqlcuộc gọi kích hoạt một trình biên dịch nhưng kế hoạch truy vấn không được lưu trong bộ nhớ cache.

Điều gì có thể là nguyên nhân của các lô / giây bằng với biên dịch / giây?

Câu trả lời:


12

Giống như mọi sp_executesqlcuộc gọi kích hoạt một biên dịch nhưng kế hoạch truy vấn không được lưu trong bộ nhớ cache.

SQL Server không lưu trữ kế hoạch truy vấn cho các lô chỉ chứa một sp_executesqlcuộc gọi. Không có kế hoạch lưu trữ, một quá trình biên dịch xảy ra mỗi lần. Đây là theo thiết kế, và dự kiến.

SQL Server tránh các lô bộ đệm với chi phí thấp để biên dịch. Các chi tiết về những gì được và không được lưu trong bộ nhớ cache đã thay đổi nhiều lần trong những năm qua. Xem câu trả lời của tôi cho cờ Trace 2861 và kế hoạch 'chi phí không' thực sự có ý nghĩa gì để biết chi tiết.

Nói tóm lại, xác suất tái sử dụng (bao gồm các giá trị tham số cụ thể) là nhỏ và chi phí biên dịch văn bản ad hoc chứa sp_executesqlcuộc gọi là rất nhỏ. Lô tham số bên trong được sản xuất bởi sp_executesqldĩ nhiên được lưu trữ và sử dụng lại - đây là giá trị của nó. Các thủ tục lưu trữ mở rộng sp_executesqlchính nó cũng được lưu trữ.

Để được lưu trữ và sử dụng lại, sp_executesqlcâu lệnh sẽ phải là một phần của một lô lớn hơn được coi là đáng lưu trữ. Ví dụ:

-- Show compilation counter
SELECT
    DOPC.[object_name],
    DOPC.cntr_value
FROM sys.dm_os_performance_counters AS DOPC
WHERE
    DOPC.counter_name = N'SQL Compilations/sec'
GO
-- This is only here to make the batch worth caching
DECLARE @TC integer =
(
    SELECT TOP (1) @@TRANCOUNT 
    FROM master.dbo.spt_values AS SV
);

-- Example call we are testing
-- (use anything for the inner query, this example uses the Stack Overflow database
EXECUTE sys.sp_executesql 
    N'SELECT LT.Type FROM dbo.LinkTypes AS LT WHERE LT.Id = @id;', 
    N'@id int', 
    @id = 1;
GO
-- Show compilation counter again
SELECT
    DOPC.[object_name],
    DOPC.cntr_value
FROM sys.dm_os_performance_counters AS DOPC
WHERE
    DOPC.counter_name = N'SQL Compilations/sec'

Chạy mã đó nhiều lần. Tuy nhiên, lần đầu tiên, nhiều phần tổng hợp được báo cáo như mong đợi. Ở lần thứ hai, không có phần tổng hợp nào được báo cáo, trừ khi optimize for ad hoc workloadsđược bật (vì vậy chỉ có Sơ đồ tổng hợp được lưu trong bộ nhớ cache). Vào lần thứ ba, không có phần tổng hợp nào được báo cáo trong mọi trường hợp, vì bất kỳ sơ khai nào cũng được thăng cấp lên một kế hoạch ad hoc được lưu trữ đầy đủ.

Xóa DECLARE @TCcâu lệnh để thấy rằng sys.sp_executesqlcâu lệnh không bao giờ được lưu trong bộ nhớ cache mà không có nó, bất kể số lần nó được thực thi.

Xem các mục bộ nhớ cache kế hoạch liên quan với:

-- Show cached plans
SELECT
    DECP.refcounts,
    DECP.usecounts,
    DECP.size_in_bytes,
    DECP.cacheobjtype,
    DECP.objtype,
    DECP.plan_handle,
    DECP.parent_plan_handle,
    DEST.[text]
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
WHERE 
    DEST.[text] LIKE N'%sp_executesql%'
    AND DEST.[text] NOT LIKE N'%dm_exec_cached_plans%';

Q & A liên quan: Do trình kích hoạt biên dịch mỗi lần?


11

Bạn có thể tính gần đúng những gì bạn thấy trong Trình theo dõi hiệu suất và Trình giám sát hoạt động SQL Compilations/secBatch Requests/sectrong khi chạy một số lô trong cửa sổ truy vấn riêng dưới dạng thử nghiệm, như chi tiết bên dưới.

Cửa sổ truy vấn 1:

DECLARE @t1 datetime;
DECLARE @t2 datetime;
DECLARE @CompVal1 int;
DECLARE @CompVal2 int;
DECLARE @ReCompVal1 int;
DECLARE @ReCompVal2 int;
DECLARE @BatchVal1 int;
DECLARE @BatchVal2 int;
DECLARE @ElapsedMS decimal(10,2);

SELECT @t1 = GETDATE()
    , @CompVal1 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Compilations/sec                                                                                                            '
        )
    , @ReCompVal1 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Re-Compilations/sec                                                                                                         '
        )
    , @BatchVal1 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'Batch Requests/sec                                                                                                              '
        );

WAITFOR DELAY '00:00:10.000';

SELECT @t2 = GETDATE()
    , @CompVal2 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Compilations/sec                                                                                                            '
        )
    , @ReCompVal2 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'SQL Re-Compilations/sec                                                                                                         '
        )
    , @BatchVal2 = (
        SELECT spi.cntr_value
        FROM sys.sysperfinfo spi
        WHERE spi.counter_name = 'Batch Requests/sec                                                                                                              '
        );

SET @ElapsedMS = DATEDIFF(MILLISECOND, @t1, @t2);
SELECT  ElapsedTimeMS = @ElapsedMS
    , [SQL Compilations/sec] = (@CompVal2 - @CompVal1) / @ElapsedMS * 1000 
    , [SQL Recompilations/sec] = (@ReCompVal2 - @ReCompVal1) / @ElapsedMS * 1000
    , [Batch Requests/sec] = (@BatchVal2 - @BatchVal1) / @ElapsedMS * 1000;

Trong Cửa sổ truy vấn 2, hãy chạy đoạn mã sau trong khi đoạn mã trên đang chạy. Mã chỉ đơn giản thực hiện 100 lô T-SQL:

EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100

Nếu bạn quay lại Cửa sổ truy vấn 1, bạn sẽ thấy một cái gì đó như thế này:

╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
ElapsedTimeMS Biên dịch SQL / giây Rec Biên dịch lại SQL / giây Request Yêu cầu hàng loạt / giây
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
10020.00 10.07984031000 0,0000000000 ║ 10.07984031000
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝

Nếu chúng ta xem xét truy vấn này:

SELECT dest.text
    , deqs.execution_count
FROM sys.dm_exec_query_stats deqs
    CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
WHERE dest.text LIKE 'SELECT TOP(1)%'

Chúng tôi có thể xác nhận đã có 100 lần thực hiện truy vấn thử nghiệm.

Trong các kết quả trên, bạn có thể thấy chúng tôi đang nhận được biên soạn mỗi lần các sp_executesqlthực thi tuyên bố. Kế hoạch cho điều đó chắc chắn đang được lưu trữ, nhưng chúng ta thấy một phần tổng hợp cho nó; đưa cái gì?

Các Microsoft Docs nói điều này về sp_executesql:

sp_executesql có hành vi tương tự như EXECUTE liên quan đến các lô, phạm vi tên và bối cảnh cơ sở dữ liệu. Câu lệnh hoặc lô Transact-SQL trong tham số sp_executesql @stmt không được biên dịch cho đến khi câu lệnh sp_executesql được thực thi. Nội dung của @stmt sau đó được biên dịch và thực thi như một kế hoạch thực hiện tách biệt với kế hoạch thực hiện của lô được gọi là sp_executesql.

Vì vậy, sp_executesql chính nó đang được biên dịch mỗi khi nó chạy, ngay cả khi kế hoạch cho văn bản lệnh đã có trong bộ đệm của kế hoạch. @PaulWhite cho thấy trong câu trả lời của mình rằng hầu hết các cuộc gọi đến sp_executesql trên thực tế không được lưu trong bộ nhớ cache.

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.