複数の実行プランを含む1つのクエリがあります。1つのプランに付与されたメモリは、2番目のプランに比べて非常に大きくなります。
この問題は、ネストされたループ結合の外部テーブルに結果を小さな入力にフィルターする述語があるが、バッチソートが外部テーブル全体と同等のカーディナリティの推定を使用しているように見える場合に発生します。これにより、過剰なメモリ認可が認識される可能性があり、同時実行サーバーでは、OOM状態、プランキャッシュエビクションのメモリ負荷、予期しないRESOURCE_SEMAPHORE待機など、いくつかの副作用が発生する可能性があります。このパターンに一致する単一のクエリが実際にハイエンドマシン(1TB + RAM)で数GBの許可されたメモリを取得する方法を見てきました。
これまでの1つのオプションは、KB 2801413で説明されているように、トレースフラグ2340を使用してこの機能をグローバルに無効にすることです。ただし、SQL Server 2016 RC0では、最適化の利点を維持するように動作を変更しましたが、今では最大付与制限がベースになっています使用可能なメモリ許可スペース。この改善は、より小さなメモリフットプリントでより多くのクエリを実行できるという意味で、より優れたスケーラビリティにもつながります。この動作を今後のSQL Server 2014 Service Pack 2に移植したこの動作をバックポーティングし、通常どおり市場にあるバージョンに付加価値を提供することを検討しています。
これはまさに私が見ているものですが、SQL Server 2016 Enterpriseを使用しています。
これらは実行計画です
https://www.brentozar.com/pastetheplan/?id=SJ0mYAy0b
https://www.brentozar.com/pastetheplan/?id=BJzutC1R-
私の質問は
2つの実行プランの理由は何ですか?
オプティマイザは上位の実行プランを使用していますが、下位のプランを使用するように強制しましたが、しばらくすると再び上位のプランに移動しますが、その理由は何ですか?
この問題を解決する方法は?この問題により、アプリケーションがクラッシュします(多数のRESOURCE_SEMAPHORE
待機し、アプリケーションが応答しなくなります)?ヒントを使用する必要があります:DISABLE_OPTIMIZED_NESTED_LOOP
またはTrace Flag 2340
?
注:私はXMLをチェックしましたが、両方のプランにNestedLoops Optimized="false"
1. 2つの実行計画の理由は何ですか?
sys.query_context_settings(Transact-SQL) ドキュメントから:
SQL Serverには、クエリのセマンティクスに影響を与える(クエリの正しい結果を定義する)コンテキスト設定がいくつかあります。異なる設定でコンパイルされた同じクエリテキストは、異なる結果を生成する可能性があります(基礎となるデータによって異なります)。
おそらく、同じクエリテキストが異なるコンテキスト設定のセッションから送信されたようです。 Erland Sommarskogによる アプリケーションで遅い、SSMSで速い? も参照してください。
2.オプティマイザは上位の実行プランを使用していますが、下位のプランを使用するように強制しましたが、しばらくすると再び上位のプランに移動しますが、その理由は何ですか。
計画 まだ時々再コンパイルされます たとえば、変更に関する通常のルールに基づきます。基礎となるオブジェクト、統計、スキーマ。クエリストアプランを強制すると、使用されるプランが sys.query_store_plan(Transact-SQL) notesのように、ソースプランと少なくとも基本的に類似であることを確認できます。 :
強制メカニズムは、このプランがquery_idによって参照されるクエリに使用されることを保証しません。プランを強制すると、クエリが再度コンパイルされ、通常、plan_idによって参照されるプランとまったく同じまたは同様のプランが生成されます。
各(再)コンパイルは、コンパイル時にオプティマイザが認識できるクエリ内の値に応じて、異なる見積もり(メモリ許可を含む)でプランを生成する場合があります。 クエリストアまたはプランガイドを使用して、特定のメモリ許可を強制することはできません。
3.この問題を修正するにはどうすればよいですか?
Dan Guzmanの answer が示唆しているように、プランが特定のパラメーター値に非常に敏感である場合、OPTION (RECOMPILE)
ヒントを使用してすべての実行で新しいプランをコンパイルすることは、最良の実用的なソリューションである可能性があります。
とは言っても、更新側の実行計画に影響を与えるために利用できるツールはほとんどなく、外部キーのカスケードやインデックス付きビューなどが関与する場合は複雑になる可能性があります。他のすべてが失敗した場合、(インデックスキーの順序で行を並べ替える)並べ替えのない実行プランが最良の結果を生成することがあります。これらのソートなしで計画を生成するために私が知っている文書化された方法はありませんが、 非文書化トレースフラグ8795 をオンにして取得した計画を強制的に試すことができます。
また、適切なMAX_GRANT_PERCENT
クエリヒント または、ワークロードを処理するのに十分なメモリをインストールする(またはSQL Serverで利用できるようにする)だけです。
どちらのプランも内部結合に_Optimized="false"
_があり、形状は同じです。参照されたブログ投稿から、外部テーブルの見積もりが許可に使用されるメモリ許可の問題は、_Optimized="true"
_でのみ適用されることを理解しています。また、これらのプランは両方とも、ネストされたループ外部テーブル(コースIDが削除されたテーブルスプール)の行カウントが1です。
私は古典的なパラメータの盗聴を疑っています。 CoursePrerequisiteAssignmentクラスター化インデックスの削除(カスケード削除?)の前のネストされたループ演算子の推定行数は、トッププランで約2,500万に対して、ボトムプランで約240万です。それがおそらく望ましいメモリを動かしているものです。
OPTION(RECOMPILE)
が最も簡単で最良の解決策かもしれません。それとは別に、コースを削除するたびにCoursePrerequisiteAssignment
テーブルのフルスキャンを回避できるようにインデックスを調整すると役立つ場合があります。