私は最近、ワークロードのタイプについて多くの会話をしました。具体的には、ワークロードがパラメーター化されているか、アドホックであるか、または混合であるかを理解しています。これは、ヘルス監査中に確認するものの1つであり、キンバリーは、プランキャッシュからの優れたクエリと、ツールキットの一部であるアドホックワークロードの投稿の最適化を行っています。以下のクエリをコピーしました。これまでに本番環境で実行したことがない場合は、実行する時間を必ず見つけてください。
SELECT objtype AS [CacheType], COUNT_BIG(*) AS [Total Plans], SUM(CAST(size_in_bytes AS DECIMAL(18, 2))) / 1024 / 1024 AS [Total MBs], AVG(usecounts) AS [Avg Use Count], SUM(CAST((CASE WHEN usecounts = 1 THEN size_in_bytes ELSE 0 END) AS DECIMAL(18, 2))) / 1024 / 1024 AS [Total MBs – USE Count 1], SUM(CASE WHEN usecounts = 1 THEN 1 ELSE 0 END) AS [Total Plans – USE Count 1] FROM sys.dm_exec_cached_plans GROUP BY objtype ORDER BY [Total MBs – USE Count 1] DESC;
このクエリを本番環境に対して実行すると、次のような出力が得られる可能性があります。
このスクリーンショットから、プランキャッシュ専用の合計が約3GBであり、そのうち1.7GBが158,000を超えるアドホッククエリのプラン用であることがわかります。その1.7GBのうち、約500MBは、 ONEを実行する125,000のプランに使用されます。 時間のみ。プランキャッシュの約1GBは、準備済みおよび手順プラン用であり、約300MB相当のスペースしか使用しません。ただし、平均使用数に注意してください。手順の場合は100万をはるかに超えています。この出力を見ると、このワークロードを混合として分類します–いくつかのパラメーター化されたクエリ、いくつかのアドホック。
Kimberlyのブログ投稿では、多くのアドホッククエリで満たされたプランキャッシュを管理するためのオプションについて説明しています。プランキャッシュの肥大化は、アドホックワークロードがある場合に対処しなければならない問題の1つにすぎません。この投稿では、発生する必要のあるすべてのコンパイルの結果としてCPUに与える影響を調査したいと思います。クエリがSQLServerで実行されると、コンパイルと最適化が行われ、このプロセスに関連するオーバーヘッドが発生します。これは、CPUコストとして現れることがよくあります。クエリプランがキャッシュに入れられると、それを再利用できます。パラメータ化されたクエリは、クエリテキストがまったく同じであるため、すでにキャッシュにあるプランを再利用することになります。アドホッククエリが実行されると、正確ながある場合にのみキャッシュ内のプランが再利用されます。 同じテキストと入力値 。
セットアップ
テストでは、TSQLでランダムな文字列を生成し、それをクエリに連結して、実行ごとに異なるリテラル値を持つようにします。これを、動的文字列実行(EXEC @QueryString)を使用してクエリを呼び出すストアドプロシージャでラップしたため、アドホックステートメントのように動作します。ストアドプロシージャ内から呼び出すことは、既知の回数実行できることを意味します。
USE [WideWorldImporters]; GO DROP PROCEDURE IF EXISTS dbo.[RandomSelects]; GO CREATE PROCEDURE dbo.[RandomSelects] @NumRows INT AS DECLARE @ConcatString NVARCHAR(200); DECLARE @QueryString NVARCHAR(1000); DECLARE @RowLoop INT = 0; WHILE (@RowLoop < @NumRows) BEGIN SET @ConcatString = CAST((CONVERT (INT, RAND () * 2500) + 1) AS NVARCHAR(50)) + CAST((CONVERT (INT, RAND () * 1000) + 1) AS NVARCHAR(50)) + CAST((CONVERT (INT, RAND () * 500) + 1) AS NVARCHAR(50)) + CAST((CONVERT (INT, RAND () * 1500) + 1) AS NVARCHAR(50)); SELECT @QueryString = N'SELECT w.ColorID, s.StockItemName FROM Warehouse.Colors w JOIN Warehouse.StockItems s ON w.ColorID = s.ColorID WHERE w.ColorName = ''' + @ConcatString + ''';'; EXEC (@QueryString); SELECT @RowLoop = @RowLoop + 1; END GO DBCC FREEPROCCACHE; GO EXEC dbo.[RandomSelects] @NumRows = 10; GO
実行後、プランキャッシュを確認すると、10個の一意のエントリがあり、それぞれにexecution_countが1であることがわかります(述語の一意の値を確認する必要がある場合は、画像を拡大表示します):
SELECT [qs].[execution_count], [qs].[sql_handle], [qs].[query_hash], [qs].[query_plan_hash], [st].[text] FROM sys.dm_exec_query_stats AS [qs] CROSS APPLY sys.dm_exec_sql_text ([qs].[sql_handle]) AS [st] CROSS APPLY sys.dm_exec_query_plan ([qs].[plan_handle]) AS [qp] WHERE [st].[text] LIKE '%Warehouse%' ORDER BY [st].[text], [qs].[execution_count] DESC; GO
ここで、同じクエリを実行するほぼ同一のストアドプロシージャを作成しますが、パラメータ化されています:
USE [WideWorldImporters]; GO DROP PROCEDURE IF EXISTS dbo.[SPRandomSelects]; GO CREATE PROCEDURE dbo.[SPRandomSelects] @NumRows INT AS DECLARE @ConcatString NVARCHAR(200); DECLARE @QueryString NVARCHAR(1000); DECLARE @RowLoop INT = 0; WHILE (@RowLoop < @NumRows) BEGIN SET @ConcatString = CAST((CONVERT (INT, RAND () * 2500) + 1) AS NVARCHAR(50)) + CAST((CONVERT (INT, RAND () * 1000) + 1) AS NVARCHAR(50)) + CAST((CONVERT (INT, RAND () * 500) + 1) AS NVARCHAR(50)) + CAST((CONVERT (INT, RAND () * 1500) + 1) AS NVARCHAR(50)) SELECT w.ColorID, s.StockItemName FROM Warehouse.Colors w JOIN Warehouse.StockItems s ON w.ColorID = s.ColorID WHERE w.ColorName = @ConcatString; SELECT @RowLoop = @RowLoop + 1; END GO EXEC dbo.[SPRandomSelects] @NumRows = 10; GO
プランキャッシュ内には、10個のアドホッククエリに加えて、10回実行されたパラメータ化されたクエリのエントリが1つ表示されます。入力はパラメータ化されているため、大きく異なる文字列がパラメータに渡されても、クエリテキストはまったく同じです。
テスト
プランキャッシュで何が起こるかを理解したので、さらに負荷を作成しましょう。 10個の異なるスレッドで同じ.sqlファイルを呼び出すコマンドラインファイルを使用します。各ファイルはストアドプロシージャを10,000回呼び出します。開始する前にプランキャッシュをクリアし、スクリプトの実行中にPerfMonを使用して合計CPU%とSQLコンパイル/秒をキャプチャします。
Adhoc.sqlファイルの内容:
EXEC [WideWorldImporters].dbo.[RandomSelects] @NumRows = 10000;
Parameterized.sqlファイルの内容:
EXEC [WideWorldImporters].dbo.[SPRandomSelects] @NumRows = 10000;
.sqlファイルを呼び出すコマンドファイルの例(メモ帳で表示):
それぞれがRun_Adhoc.cmdファイルを呼び出す10個のスレッドを作成するコマンドファイルの例(メモ帳で表示):
クエリの各セットを合計100,000回実行した後、プランキャッシュを見ると、次のことがわかります。
プランキャッシュには10,000を超えるアドホックプランがあります。実行された100,000のアドホッククエリすべてのプランがないのはなぜか疑問に思われるかもしれません。それは、プランキャッシュの動作(使用可能なメモリに基づくサイズ、未使用のプランが期限切れになった場合など)に関係しています。重要なのはそう 残りのキャッシュタイプで見られるものと比較して、多くのアドホックプランが存在します。
以下にグラフ化されたPerfMonデータは、最もわかりやすいものです。 100,000個のパラメーター化されたクエリの実行は15秒未満で完了し、最初はコンパイル/秒にわずかなスパイクがありましたが、これはグラフではほとんど目立ちません。同じ数のアドホック実行が完了するまでに60秒強かかり、コンパイル/秒が2000近くで急上昇した後、45秒のマークのあたりで1000に近づき、CPUはほとんどの時間100%に近いか100%になりました。
概要
テストは非常に単純で、1つのバリエーションのみを送信しました。 アドホッククエリ。一方、本番環境では、数百または数千の数百または数千の異なるバリエーションがあります。 さまざまなアドホッククエリのこれらのアドホッククエリのパフォーマンスへの影響は、発生するプランキャッシュの肥大化だけではありませんが、現在のワークロードのタイプに精通していない場合は、プランキャッシュを確認することから始めるのが最適です。大量のアドホッククエリはコンパイルを促進する可能性があるため、CPUを駆動する可能性があります。これは、ハードウェアを追加することでマスクされる場合がありますが、CPUがボトルネックになる可能性があります。これが環境の問題または潜在的な問題である可能性があると思われる場合は、最も頻繁に実行されているアドホッククエリを特定し、それらをパラメータ化するためのオプションを確認してください。誤解しないでください。パラメータ化されたクエリには潜在的な問題があり(データの偏りによる計画の安定性など)、これは解決しなければならないもう1つの問題です。ワークロードに関係なく、コーディング、構成、メンテナンスなどの「設定して忘れる」方法はめったにないことを理解することが重要です。SQLServerソリューションは生きており、常に変化し、継続的なケアとフィードを行うエンティティを呼吸しています。確実に実行します。 DBAのタスクの1つは、その変化を常に把握し、パフォーマンスを可能な限り管理することです。これは、アドホックなパフォーマンスの課題に関連する場合でも、パラメーター化されたパフォーマンスの課題に関連する場合でも同様です。