Setiap batch menyebabkan kompilasi

10

Kami memiliki aplikasi pihak ketiga yang mengirimkan pernyataan T-SQL dalam batch.

Basis data di-host pada SQL Server 2016 Enterprise SP1 CU7, 16 core dan memori 256GB. Optimize for Ad-Hoc diaktifkan.

Ini adalah contoh tiruan dari kueri yang dieksekusi:

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'

Ketika saya memonitor database dan saya melihat batch / detik dan mengkompilasi / detik, saya perhatikan mereka selalu sama. Di bawah beban berat, ini bisa 1000 batch / detik dan 1000 kompilasi / detik. Di bawah beban rata-rata, ada 150 batch / detik.

Saya menganalisis cache permintaan untuk paket yang baru saja dikompilasi:

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;

Ketika saya menjalankan kueri di atas, saya hanya melihat 10-20 paket / detik permintaan baru.

Ini seperti setiap sp_executesqlpanggilan memicu kompilasi tetapi rencana kueri tidak di-cache.

Apa yang bisa menjadi penyebab batch / detik sama dengan kompilasi / detik?

Frederik Vanderhaegen
sumber

Jawaban:

12

Ini seperti setiap sp_executesqlpanggilan memicu kompilasi tetapi rencana kueri tidak di-cache.

SQL Server tidak men - cache rencana permintaan untuk kumpulan yang hanya berisi sp_executesqlpanggilan. Tanpa rencana cache, kompilasi terjadi setiap waktu. Ini dengan desain, dan diharapkan.

SQL Server menghindari kumpulan caching dengan biaya rendah untuk dikompilasi. Detail tentang apa yang ada dan tidak di-cache telah berubah berkali-kali selama bertahun-tahun. Lihat jawaban saya untuk Trace flag 2861 dan apa arti rencana 'tanpa biaya' untuk detail.

Singkatnya, kemungkinan penggunaan kembali (termasuk nilai parameter tertentu) kecil, dan biaya kompilasi teks ad hoc yang berisi sp_executesqlpanggilan sangat kecil. Batch parameterisasi dalam yang diproduksi oleh sp_executesqltentu saja di-cache dan digunakan kembali - ini adalah nilainya. Prosedur tersimpan yang diperpanjang sp_executesqlitu sendiri juga di-cache.

Untuk di-cache dan digunakan kembali, sp_executesqlpernyataan itu harus menjadi bagian dari batch yang lebih besar yang dianggap layak untuk di-cache. Sebagai contoh:

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

Jalankan kode itu beberapa kali. Namun untuk pertama kalinya, banyak kompilasi dilaporkan seperti yang diharapkan. Pada kedua kalinya, tidak ada kompilasi yang dilaporkan, kecuali optimize for ad hoc workloadsdiaktifkan (jadi hanya Stub Plan yang Dikompilasi yang di -cache). Pada ketiga kalinya, tidak ada kompilasi yang dilaporkan dalam kasus apa pun, karena rintisan apa pun dipromosikan ke rencana ad hoc yang sepenuhnya di-cache.

Hapus DECLARE @TCpernyataan untuk melihat bahwa sys.sp_executesqlpernyataan tidak pernah di-cache tanpa itu, terlepas dari berapa kali dieksekusi.

Lihat entri cache paket terkait dengan:

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

Tanya Jawab Terkait: Apakah pemicu kompilasi setiap kali?

Paul White 9
sumber
11

Anda dapat memperkirakan apa yang Anda lihat di Monitor Kinerja dan Monitor Aktivitas untuk SQL Compilations/secdan Batch Requests/sec, saat menjalankan beberapa kumpulan di jendela kueri terpisah sebagai tes, seperti yang dijelaskan di bawah ini.

Jendela Kueri 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;

Di Jendela Kueri 2, jalankan yang berikut saat kode di atas sedang berjalan. Kode hanya menjalankan 100 batch T-SQL:

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

Jika Anda kembali ke Jendela Kueri 1 Anda akan melihat sesuatu seperti ini:

╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ SQL Compilations / sec ║ SQL Recompilations / sec ║ Permintaan Batch / detik ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
║ 10020.00 ║ 10.07984031000 ║ 0.00000000000 ║ 10.07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝

Jika kita melihat kueri ini:

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

Kami dapat mengonfirmasi ada 100 eksekusi permintaan tes.

Dalam hasil di atas, Anda dapat melihat kita semakin kompilasi setiap kali itu sp_executesqlmengeksekusi pernyataan. Rencana untuk itu tentunya sedang di-cache, namun kami melihat kompilasi untuk itu; apa yang memberi

The Microsoft Docs mengatakan ini tentang sp_executesql:

sp_executesql memiliki perilaku yang sama dengan EXECUTE berkaitan dengan batch, lingkup nama, dan konteks database. Pernyataan Transact-SQL atau kumpulan dalam parameter sp_executesql @stmt tidak dikompilasi sampai pernyataan sp_executesql dieksekusi. Isi dari @stmt kemudian dikompilasi dan dieksekusi sebagai rencana eksekusi yang terpisah dari rencana eksekusi batch yang disebut sp_executesql.

Jadi, sp_executesql itu sendiri sedang dikompilasi setiap kali dijalankan, bahkan jika rencana untuk teks perintah sudah ada dalam cache rencana. @ PaulWhite menunjukkan dalam jawabannya bahwa sebagian besar panggilan ke sp_executesql sebenarnya tidak di-cache.

Max Vernon
sumber