T-SQLステートメントをバッチで送信するサードパーティアプリケーションがあります。
データベースは、SQL Server 2016 Enterprise SP1 CU7、16コア、256 GBメモリでホストされています。アドホックの最適化が有効になっています。
これは、実行されているクエリのダミーの例です。
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;
上記のクエリを実行すると、1秒あたり10〜20の新しいクエリプランしか表示されません。
それはすべてのsp_executesql
呼び出しはコンパイルをトリガーしますが、クエリプランはキャッシュされません。
バッチ/秒がコンパイル/秒と同じになる原因は何ですか?
すべての
sp_executesql
呼び出しがコンパイルをトリガーするようですが、クエリプランはキャッシュされません。
SQL Serverは、sp_executesql
呼び出しのみを含むバッチのクエリプランをキャッシュしません。キャッシュされたプランがないと、毎回コンパイルが行われます。これは仕様によるものであり、予想どおりです。
SQL Serverは、コンパイルにかかるコストが低いバッチのキャッシュを回避します。キャッシュされるものとキャッシュされないものの詳細は、長年にわたって何度も変化しています。詳細については、私の回答 トレースフラグ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'
そのコードを数回実行します。ただし、初めて、多くのコンパイルが期待どおりに報告されます。 2回目は、optimize for ad hoc workloads
が有効になっていない限り、コンパイルは報告されません(つまり、Compiled Plan Stubのみがキャッシュされます)。 3回目は、どのスタブも完全にキャッシュされたアドホックプランに昇格されるため、コンパイルは報告されません。
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%';
関連するQ&A: トリガーは毎回コンパイルされますか?