Кожна партія викликає компіляцію


10

У нас є стороннє додаток, яке надсилає операції T-SQL партіями.

База даних розміщується на SQL Server 2016 Enterprise SP1 CU7, 16 ядер та 256 ГБ пам'яті. Увімкнено оптимізацію для спеціальної роботи.

Це фіктивний приклад запитів, які виконуються:

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'

Коли я стежу за базою даних і переглядаю партії / сек та компіляції / сек, то помічаю, що вони завжди однакові. При великому навантаженні це може бути 1000 партій / сек і 1000 компіляцій / сек. При середньому навантаженні 150 партій / сек.

Я аналізую кеш запитів для нещодавно складених планів:

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;

Коли я перебігаю над запитом, я бачу лише 10-20 нових планів запитів / сек.

Це як кожен sp_executesqlвиклик запускає компіляцію, але план запитів не кешований.

Що може бути причиною того, що партії / сек дорівнюють компіляціям / сек?

Відповіді:


12

Це як кожен sp_executesqlвиклик запускає компіляцію, але план запитів не кешований.

SQL Server не кешує план запитів для пакетів, що містять лише sp_executesqlвиклик. Без кешованого плану щоразу відбувається компіляція. Це задумом і очікуваним.

SQL Server уникає кешування пакетів з низькою вартістю для компіляції. Деталі того, що є, а що не є кешованим, змінювалися багато разів за ці роки. Дивіться мою відповідь на прапор "Trace 2861" і що насправді означає "нульова вартість" для деталей.

Коротше кажучи, ймовірність повторного використання (включаючи конкретні значення параметрів) невелика, а вартість складання спеціального тексту, що містить sp_executesqlвиклик, дуже мала. Внутрішня параметризована партія, вироблена компанією sp_executesql, звичайно є кешованою та повторно використаною - у цьому її значення. Сама керована збережена процедура sp_executesqlтакож кешується.

Для кешування та повторного використання sp_executesqlоператор повинен бути частиною більшої партії, яка вважається вартістю кешування. Наприклад:

-- 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'

Запустіть цей код кілька разів. Однак уперше про багато компіляцій повідомляється як очікувалося. Вдруге про компіляції не повідомляється, якщо optimize for ad hoc workloadsце не ввімкнено (тому кешується лише компільований план Stub ). По-третє, про жодні компіляції не повідомляється, оскільки будь-яка заглушка просувається до повністю кешованого спеціального плану.

Вийміть DECLARE @TCоператор, щоб побачити, що sys.sp_executesqlоператор ніколи не кешується без нього, незалежно від кількості разів його виконання.

Перегляньте пов'язані записи кешу плану за допомогою:

-- 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%';

Питання та відповіді: Чи тригери збираються щоразу?


11

Ви можете наблизити те, що ви бачите в Моніторі продуктивності та Моніторі діяльності, SQL Compilations/secі Batch Requests/sec, виконуючи деякі партії, в окремому вікні запитів, як тест, як детально описано нижче.

Вікно запиту 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;

У вікні запитів 2 виконайте такі дії, поки працює вищевказаний код. Код просто виконує 100 пакетів T-SQL:

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

Якщо ви повернетесь до вікна запитів 1, ви побачите щось подібне:

╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ Компіляції SQL / сек ║ Рекомпіляції SQL / сек ║ Пакетні запити / сек ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
║ 10020,00 ║ 10.07984031000 ║ 0,00000000000 ║ 10.07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝

Якщо ми подивимось на цей запит:

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)%'

Ми можемо підтвердити, що було 100 виконань тестового запиту.

У наведених вище результати, ви можете бачити , ми отримуємо компіляції кожен раз , коли в sp_executesqlвиконання оператора. План для цього, безумовно, кешований, але ми бачимо компіляцію для цього; що дає?

У Docs Microsoft сказати про sp_executesql:

sp_executesql має таку саму поведінку, що і EXECUTE щодо пакетів, обсягу імен та контексту бази даних. Оператор або пакетна команда Transact-SQL в параметрі sp_executesql @stmt не компілюється, поки не буде виконано оператор sp_executesql. Вміст @stmt потім компілюється та виконується у вигляді плану виконання окремо від плану виконання партії, що називається sp_executesql.

Отже, sp_executesql сам складається під час кожного запуску, навіть якщо план командного тексту вже знаходиться в кеші плану. @PaulWhite у своїй відповіді показує, що більшість дзвінків до sp_executesql насправді не кешовані.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.