web-dev-qa-db-ja.com

OPTION(OPTIMIZE FOR UNKNOWN)とOPTION(RECOMPILE)の主な違いは何ですか?

SQL Server 2012で古典的な Parameter Sniffing の問題に遭遇しました。いくつかの調査に基づいて、この問題の周りに複数のオプションが見つかりました。違いを理解するために必要な2つのオプションは、OPTION(OPTIMIZE FOR UNKNOWN)OPTION(RECOMPILE)です。

この問題が発生しているクエリの最後でOPTION(RECOMPILE)を使用することをためらっています。これは、サーバーに毎回新しい実行プランを生成させるためです。このクエリを頻繁に呼び出すと、そのマシンのCPUが急上昇します。

彼が利用できる最良のソリューションを使用するために、2つのオプションの実際の違いは何ですか?

OPTION(OPTIMIZE FOR UNKNOWN)は毎回再コンパイルする代わりにキャッシュを再利用しますか?

14
Junior

OPTION(OPTIMIZE FOR UNKNOWN)は毎回再コンパイルする代わりにキャッシュを再利用しますか?

はい、そうです。


[〜#〜] msdn [〜#〜] からのこの引用からわかるように、OPTION(OPTIMIZE FOR UNKNOWN)OPTION(RECOMPILE)の間には2つの主な違いがあります。

_OPTIMIZE FOR UNKNOWN_

クエリをコンパイルして最適化するときに、強制パラメーター化で作成されたパラメーターを含め、すべてのローカル変数の初期値ではなく統計データを使用するようにクエリオプティマイザーに指示します。

RECOMPILE

SQL Serverデータベースエンジンに対して、クエリの実行後に生成されたプランを破棄するように指示します。クエリオプティマイザーは、同じクエリが次に実行されるときにクエリプランを強制的に再コンパイルします。 RECOMPILEを指定しないと、データベースエンジンはクエリプランをキャッシュして再利用します。クエリプランをコンパイルするとき、RECOMPILEクエリヒントはクエリ内のローカル変数の現在の値を使用し、クエリがストアドプロシージャ内にある場合は、現在の値が任意のパラメーターに渡されます。

したがって、2つの主な違いは次のとおりです。

  1. クエリプランのキャッシュ(またはキャッシュしない)。

通常、生成されたクエリプランはキャッシュされ、再利用されます。 _OPTIMIZE FOR UNKNOWN_は、エンジンのこの機能には影響しません。 RECOMPILEはこの機能を抑制し、エンジンにプランを破棄してキャッシュに入れないように指示します。

  1. 計画の生成中に実際のパラメーター値を使用する(または使用しない)。

通常、オプティマイザはパラメータ値を「嗅ぎ」、計画を生成するときにこれらの値を使用します。 _OPTIMIZE FOR UNKNOWN_はこの機能を抑制し、すべてのパラメーターを値が不明であるかのように処理するようにエンジンに指示します。オプティマイザーには、さまざまなフィルタリング基準に利用可能な統計を使用する方法とヒューリスティックが組み込まれています。詳細は 最適化…平凡? を参照してください。通常、パラメータースニッフィングは、クエリ/ストアドプロシージャの最初の実行時に使用され、最初の実行中にパラメーターの値を使用します。生成されたプランはキャッシュされ、後で再利用できます。

ここで覚えておくべき非自明なことの1つは、両方の場合(通常、クエリヒントなし、および_OPTIMIZE FOR UNKNOWN_ヒ​​ントあり)で、生成されたプランが有効であり、anyの正しい結果を生成する必要があることです可能なパラメーター値。通常/ヒントなしのケースで、最初の実行時に使用されたスニッフィングされた値に合わせて調整されます。 _OPTIMIZE FOR UNKNOWN_の場合は特定の値に合わせて調整されていませんが、後でパラメーターが変更された場合は引き続き有効です。

これは重要であり、オプティマイザが計画の特定の変換および簡略化を実行するのを防ぎます。

OPTION(RECOMPILE)を使用すると、オプティマイザは各実行中にパラメータの実際の値をインライン化でき、オプティマイザはパラメータの実際の値を使用してより適切な計画を生成します。プランがキャッシュされて再利用されないため、生成されたプランが他のパラメーター値で機能しない可能性があることを心配する必要はありません。

この効果は、主に 動的検索条件 クエリで表示されます。例えば:

_SELECT ...
FROM T
WHERE
    (@ParamSomeID = 0)
    OR
    (
        @ParamSomeID = -1
        AND
        T.SomeID NOT IN
        (
            SELECT OtherTable.SomeID
            FROM OtherTable
        )
    )
    OR
    (
        T.SomeID IN
        (
            SELECT OtherTable.SomeID
            FROM OtherTable
            WHERE OtherTable.SomeID = @ParamSomeID
        )
    )
OPTION(RECOMPILE)
_

_@ParamSomeID_が_0_の場合、オプティマイザはクエリにWHERE句がまったくないかのように処理します。この計画では、OtherTableについてはまったく触れられていません。

_@ParamSomeID_が_-1_の場合、プランは、左反セミ結合を使用してTOtherTableに結合し、OtherTable全体をスキャンします。

_@ParamSomeID_がたとえば5の場合、プランはOtherTableの一意のインデックスでインデックスシークを実行し、OtherTableから1行のみを読み取ります。

OPTION(RECOMPILE)がないと、この種の単純化と変換は起こりません。

OPTION(RECOMPILE)を使用するもう1つの理由は、データ分布が非常に歪んでいる場合です。たとえば、100万行のテーブルがあるとします。 1つの列の値は、990K行で0、1K行で1〜10です。この列でフィルターをかけるクエリには、フィルターの実際の値に応じて異なるプランが必要です。

上記の両方の例で、_OPTIMIZE FOR UNKNOWN_は平凡な計画を生成します。

13

OPTION(OPTIMIZE FOR UNKNOWN)は毎回再コンパイルする代わりにキャッシュを再利用しますか?

はい。不明の最適化は、プランの生成方法に影響します(つまり、明示的にパラメーターをスニッフィングして列データのヒストグラムと比較します)が、生成されると、プランはキャッシュに残り、再利用されます。

OPTION(RECOMPILE)は、実行のたびに強制的に再コンパイルします。これは、かなり強引なアプローチです。これは、各クエリが異なり、複雑で、実行時間がかなり長い分析DW/BI環境でのみ意味があります。

あなたはあなたの処分で他のオプションも持っています:

これらの両方で、投稿と同じ効果を得ることができますが、非侵襲的な方法で行われます(アプリのコードやクエリの変更はありません)。

10
Remus Rusanu

私は両方を使用しました。 OPTION(OPTIMIZE FOR UNKNOWN)は、さまざまなパラメーターを取り込む重い検索のストアドプロシージャに使用されました。私にはわからない特定の条件(統計とそうでないもの)があり、最適化が無効になり、クエリは平凡でしたが、深刻な遅延(およびタイムアウト)が発生しました。 OPTION(OPTIMIZE FOR UNKNOWN)はこの問題を解決しましたが、理想的ではありませんでした。

同じ重い検索手順では断続的な問題が発生します。つまり、数か月後に検索がタイムアウトします。当面の解決策は、_sp_recompile_を呼び出すことです。これは、OPTION(RECOMPILE)句をストアドプロシージャに追加することと同義です。

ストアドプロシージャの根性が"入力した結果"ソリューションを推進しました。このソリューションでは、3回のキーストロークごとにDB検索がトリガーされ、結果がドロップダウンに表示されます。

最後に、OPTION(OPTIMIZE FOR UNKNOWN)を削除し、_EXEC sp_recompile<sp>_を毎晩のメンテナンスジョブに追加するだけで、すべての問題が解決しました。

2
Ross Bush