sql >> データベース >  >> RDS >> Database

ハロウィーンの問題–パート4

    [パート1|パート2|パート3|パート4]

    ハロウィーン問題は、実行計画に多くの重要な影響を与える可能性があります。シリーズのこの最後のパートでは、データを追加、変更、または削除するクエリのプランをコンパイルするときに、オプティマイザーがハロウィーンの問題を回避するために採用できるトリックについて説明します。

    背景

    何年にもわたって、ハロウィーンの問題を回避するために多くのアプローチが試みられてきました。初期の手法の1つは、同じインデックスのキーからの読み取りとキーへの書き込みを伴う実行計画の作成を単純に回避することでした。これは、パフォーマンスの観点からはあまり成功しませんでした。特に、選択的な非クラスター化インデックスを使用して変更する行を見つける代わりに、ベーステーブルをスキャンすることを意味することが多かったためです。

    2番目のアプローチは、最初に変更の対象となるすべての行を見つけてどこかに保存し、次に変更の実行を開始することによって、更新クエリの読み取りフェーズと書き込みフェーズを完全に分離することでした。 SQL Serverでは、この完全な相分離 これは、今ではおなじみのEagerTableSpoolを更新演算子の入力側に配置することで実現されます。

    スプールは入力からすべての行を読み取り、それらを非表示の tempdbに格納します 仕事台。この作業テーブルのページはメモリに残っている可能性があります。行のセットが大きい場合、またはサーバーにメモリ不足がある場合は、物理ディスク領域が必要になる可能性があります。

    通常、パイプラインとして可能な限り多くの計画を実行する必要があるため、完全な相分離は理想的とは言えません。各行は、次の行に進む前に完全に処理されます。パイプラインには、一時的な保管の必要性を回避することや、各行に1回だけ触れることなど、多くの利点があります。

    SQLServerオプティマイザー

    SQL Serverは、これまでに説明した2つの手法よりもはるかに進んでいますが、もちろんオプションとして両方が含まれています。 SQL Serverクエリオプティマイザは、Halloween Protectionを必要とするクエリを検出し、どれだけを決定します。 保護が必要であり、コストベースを使用します その保護を提供する最も安価な方法を見つけるための分析。

    ハロウィーン問題のこの側面を理解する最も簡単な方法は、いくつかの例を見ることです。次のセクションでのタスクは、既存のテーブルに数値の範囲を追加することですが、まだ存在していない数値のみです。

    CREATE TABLE dbo.Test
    (
        pk      integer NOT NULL,
     
        CONSTRAINT PK_Test
            PRIMARY KEY CLUSTERED (pk)
    );

    5行

    最初の例では、1から5までの範囲の数値を処理します。

    INSERT dbo.Test (pk)
    SELECT Num.n 
    FROM dbo.Numbers AS Num
    WHERE
        Num.n BETWEEN 1 AND 5
        AND NOT EXISTS 
        (
            SELECT NULL
            FROM dbo.Test AS t 
            WHERE t.pk = Num.n
        );

    このクエリは、テストテーブルの同じインデックスのキーの読み取りと書き込みを行うため、実行プランにはハロウィーン保護が必要です。この場合、オプティマイザはEagerTableSpoolを使用した完全相分離を使用します。

    50行

    テストテーブルに5行が含まれているので、同じクエリを再度実行して、WHEREを変更します。 1から50までの数値を処理する句

    このプランは、ハロウィーンの問題に対する正しい保護を提供しますが、熱心なテーブルスプールを備えていません。オプティマイザは、ハッシュ一致結合演算子がビルド入力をブロックしていることを認識します。オペレーターがプローブ入力からの行を使用してマッチングプロセスを開始する前に、すべての行がハッシュテーブルに読み込まれます。結果として、この計画は、スプールを必要とせずに(テストテーブルのみの)相分離を自然に提供します。

    オプティマイザーは、コストベースの理由から、5行プランに見られるネストされたループの参加よりもハッシュマッチの参加プランを選択しました。 50行のハッシュマッチプランの推定総費用は0.0347345です。 ユニット。オプティマイザーがネストされたループを選択しなかった理由を確認するためのヒントを使用して、以前に使用したネストされたループの計画を強制できます。

    このプランの推定費用は0.0379063です。 スプールを含むユニット、ハッシュマッチプランより少し多い。

    500行

    テストテーブルに50行が追加されたので、数値の範囲をさらに 500に増やします。 :

    今回は、オプティマイザーがマージ結合を選択しますが、ここでもEagerTableSpoolはありません。ソート演算子は、この計画で必要な相分離を提供します。最初の行を返す前に入力を完全に消費します(すべての行が表示されるまで、ソートはどの行が最初にソートされるかを知ることができません)。オプティマイザーは、 50の並べ替えを決定しました テストテーブルの行は、熱心なスプーリング 450よりも安価です。 更新演算子の直前の行。

    並べ替えとマージの結合プランの推定コストは0.0362708です。 ユニット。ハッシュマッチとネストされたループプランの代替案は、 0.0385677で発表されます。 ユニットと0.112433 それぞれユニット。

    並べ替えについて奇妙なこと

    これらの例を自分で実行している場合、特にテストテーブルのシークと並べ替えに関するプランエクスプローラーのツールチップを見ると、最後の例について奇妙なことに気付いたかもしれません。

    シークは注文を生成します pkのストリーム 値では、直後に同じ列で並べ替えるポイントは何ですか?その(非常に合理的な)質問に答えるために、まずSELECTだけを見ていきます。 INSERTの一部 クエリ:

    SELECT Num.n 
    FROM dbo.Numbers AS Num
    WHERE
        Num.n BETWEEN 1 AND 500
        AND NOT EXISTS 
        (
            SELECT 1
            FROM dbo.Test AS t 
            WHERE t.pk = Num.n
        )
    ORDER BY
        Num.n;
    >

    このクエリは、以下の実行プランを生成します(ORDER BYの有無にかかわらず 私はあなたが持っているかもしれない特定の技術的な異議に対処するために追加しました):

    ソート演算子がないことに注意してください。では、なぜINSERT 計画には並べ替えが含まれますか?単にハロウィーンの問題を回避するためです。オプティマイザーは、冗長な並べ替えを実行することを検討しました (組み込みの相分離を使用)は、クエリを実行して正しい結果を保証するための最も安価な方法でした。賢い。

    ハロウィーンの保護レベルとプロパティ

    SQL Serverオプティマイザーには、クエリプランの各ポイントで必要なHalloween Protection(HP)のレベルと、各オペレーターが持つ詳細な効果について推論できる特定の機能があります。これらの追加機能は、オプティマイザーが検索アクティビティ中に他の何百もの重要な情報を追跡するために使用するのと同じプロパティフレームワークに組み込まれています。

    各オペレーターには必須があります HPプロパティと配信済み HPプロパティ。 必須 プロパティは、正しい結果を得るためにツリー内のその時点で必要なHPのレベルを示します。 配信済み プロパティは、現在のオペレーターが提供するHPと累積を反映しています サブツリーによって提供されるHP効果。

    オプティマイザには、各物理演算子(Compute Scalarなど)がHPレベルにどのように影響するかを決定するロジックが含まれています。さまざまな代替プランを検討し、提供されたHPが更新オペレーターで必要なHPよりも少ないプランを拒否することにより、オプティマイザーは、必ずしも熱心なテーブルスプールを必要としない正確で効率的なプランを柔軟に見つけることができます。

    ハロウィーン保護の計画変更

    前のマージ参加の例では、オプティマイザーがハロウィーン保護の冗長な並べ替えを追加するのを見ました。これが単純なEagerTableSpoolよりも効率的であることをどのように確認できますか?そして、更新計画のどの機能がハロウィーン保護のためだけにあるのかをどうやって知ることができますか?

    両方の質問は、文書化されていないトレースフラグ 8692 を使用して(当然、テスト環境で)回答できます。 、これにより、オプティマイザはハロウィーン保護のために熱心なテーブルスプールを使用するように強制されます。冗長ソートを使用したマージ結合プランの推定コストは0.0362708であったことを思い出してください。 マジックオプティマイザーユニット。トレースフラグ8692を有効にしてクエリを再コンパイルすることにより、これをEagerTableSpoolの代替案と比較できます。

    INSERT dbo.Test (pk)
    SELECT Num.n 
    FROM dbo.Numbers AS Num
    WHERE
        Num.n BETWEEN 1 AND 500
        AND NOT EXISTS 
        (
            SELECT 1
            FROM dbo.Test AS t 
            WHERE t.pk = Num.n
        )
    OPTION (QUERYTRACEON 8692);

    EagerSpoolプランの推定コストは0.0378719です。 ユニット( 0.0362708から増加 冗長ソート付き)。ここに示されているコストの違いは、タスクの些細な性質と行のサイズが小さいため、それほど重要ではありません。複雑なツリーとより多くの行数を使用する実際の更新クエリは、SQL Serverオプティマイザーがハロウィーンの保護について深く考えることができるため、はるかに効率的な計画を作成することがよくあります。

    その他の非スプールオプション

    計画内でブロッキングオペレーターを最適に配置することは、ハロウィーンの問題に対する保護を提供するコストを最小限に抑えるためにオプティマイザーに開かれた唯一の戦略ではありません。次の例が示すように、処理される値の範囲についても推論できます。

    CREATE TABLE #Test
    (
        pk          integer IDENTITY PRIMARY KEY,
        some_value  integer
    );
     
    CREATE INDEX i ON #Test (some_value);
     
    -- Pretend the table has lots of data in it
    UPDATE STATISTICS #Test
    WITH ROWCOUNT = 123456, PAGECOUNT = 1234;
     
    UPDATE #Test
    SET some_value = 10
    WHERE some_value = 5;

    共通インデックスのキーを読み取って更新しているにもかかわらず、実行プランではハロウィーン保護の必要はありません。

    オプティマイザは、「some_value」を5から10に変更しても、更新された行がインデックスシーク(some_valueが5の行のみを検索する)によって2回目に表示されることはないことを確認できます。この推論は、リテラル値がクエリで使用されている場合、またはクエリがOPTION (RECOMPILE)を指定している場合にのみ可能です。 、オプティマイザが1回限りの実行プランのパラメータの値をスニッフィングできるようにします。

    クエリにリテラル値がある場合でも、データベースオプションFORCED PARAMETERIZATIONの場合、オプティマイザはこのロジックを適用できない可能性があります。 ONです 。その場合、クエリのリテラル値はパラメータに置き換えられ、オプティマイザはハロウィーン保護が不要であることを確認できなくなります(または、プランが別のパラメータ値で再利用される場合は不要になります):

    FORCED PARAMETERIZATIONがどうなるか疑問に思っている場合は、 有効になっているおよび クエリはOPTION (RECOMPILE)を指定します 、答えは、オプティマイザがスニッフィングされた値の計画をコンパイルするため、最適化を適用できるということです。いつものようにOPTION (RECOMPILE) 、特定の値のクエリプランは再利用のためにキャッシュされません。

    トップ

    この最後の例は、Top オペレーターはハロウィーン保護の必要性を取り除くことができます:

    UPDATE TOP (1) t
    SET some_value += 1
    FROM #Test AS t
    WHERE some_value <= 10;

    1行のみを更新するため、保護は必要ありません。最初の行が更新されるとすぐに処理パイプラインが停止するため、インデックスシークは更新された値を検出できません。この場合も、この最適化は、定数リテラル値がTOPで使用されている場合にのみ適用できます。 、または値「1」を返す変数がOPTION (RECOMPILE)を使用してスニッフィングされた場合 。

    TOP (1)を変更した場合 TOP (2)へのクエリで 、オプティマイザーは、インデックスシークの代わりにクラスター化インデックススキャンを選択します:

    クラスター化されたインデックスのキーは更新されないため、このプランではハロウィーン保護は必要ありません。 TOP (2)のヒントを使用して非クラスター化インデックスの使用を強制する クエリにより、保護のコストが明らかになります:

    オプティマイザーは、クラスター化インデックススキャンがこのプランよりも安価であると推定しました(追加のハロウィーン保護を使用)。

    オッズとエンド

    これまでシリーズで自然な場所を見つけられなかったハロウィーン保護について私が言いたい他のいくつかのポイントがあります。 1つ目は、行バージョン管理の分離レベルが使用されている場合のハロウィーン保護の問題です。

    行のバージョン管理

    SQL Serverには、READ COMMITTED SNAPSHOTという2つの分離レベルがあります。 およびSNAPSHOT ISOLATION tempdbのバージョンストアを使用する データベースのステートメントレベルまたはトランザクションレベルの一貫したビューを提供します。バージョンストアは、現在実行中のステートメントがこれまでに行った変更の影響を受けないデータを提供できるため、SQLServerはこれらの分離レベルでHalloweenProtectionを完全に回避できます。このアイデアは現在、リリースされたバージョンのSQL Serverには実装されていませんが、Microsoftはこれがどのように機能するかを説明する特許を申請しているため、将来のバージョンにはこのテクノロジが組み込まれる可能性があります。

    ヒープと転送されたレコード

    ヒープ構造の内部に精通している場合は、転送されたレコードがヒープテーブルで生成されるときに、特定のハロウィーンの問題が発生する可能性があるかどうか疑問に思うかもしれません。これが初めての場合、既存の行が元のデータページに収まらないように更新されると、ヒープレコードが転送されます。エンジンは転送スタブを残し、展開されたレコードを別のページに移動します。

    ヒープスキャンを含むプランが転送されるようにレコードを更新すると、問題が発生する可能性があります。スキャン位置が転送されたレコードのあるページに到達すると、ヒープスキャンが再び行に遭遇する可能性があります。 SQL Serverでは、ストレージエンジンが常に転送ポインターをすぐに追跡することを保証しているため、この問題は回避されます。スキャンで転送されたレコードが検出された場合、スキャンはそれを無視します。このセーフガードが設定されていれば、クエリオプティマイザはこのシナリオについて心配する必要はありません。

    SCHEMABINDINGおよびT-SQLスカラー関数

    T-SQLスカラー関数を使用することをお勧めする場合はほとんどありませんが、使用する必要がある場合は、ハロウィーン保護に関して重要な影響があることに注意する必要があります。スカラー関数がSCHEMABINDINGで宣言されていない限り オプションの場合、SQLServerは関数がテーブルにアクセスすると想定します。説明のために、以下の単純なT-SQLスカラー関数について考えてみます。

    CREATE FUNCTION dbo.ReturnInput
    (
        @value integer
    )
    RETURNS integer
    AS
    BEGIN
    	RETURN @value;
    END;

    この関数はテーブルにアクセスしません。実際には、渡されたパラメータ値を返す以外は何もしません。次のINSERTを見てください クエリ:

    DECLARE @T AS TABLE (ProductID integer PRIMARY KEY);
     
    INSERT @T (ProductID)
    SELECT p.ProductID
    FROM AdventureWorks2012.Production.Product AS p;

    実行計画は私たちが期待するとおりであり、ハロウィーンの保護は必要ありません:

    ただし、何もしない機能を追加すると、劇的な効果があります。

    DECLARE @T AS TABLE (ProductID integer PRIMARY KEY);
     
    INSERT @T (ProductID)
    SELECT dbo.ReturnInput(p.ProductID)
    FROM AdventureWorks2012.Production.Product AS p;

    実行プランには、ハロウィーン保護のための熱心なテーブルスプールが含まれるようになりました。 SQL Serverは、関数がデータにアクセスすることを前提としています。これには、Productテーブルからの読み取りが含まれる場合があります。ご存知かもしれませんが、INSERT プランの読み取り側にターゲットテーブルへの参照が含まれているプラ​​ンには、完全なハロウィーン保護が必要です。オプティマイザーが知る限り、これが当てはまる可能性があります。

    SCHEMABINDINGを追加する 関数定義のオプションは、SQLServerが関数の本体を調べてアクセスするテーブルを決定することを意味します。そのようなアクセスは検出されないため、ハロウィーン保護は追加されません:

    ALTER FUNCTION dbo.ReturnInput
    (
        @value integer
    )
    RETURNS integer
    WITH SCHEMABINDING
    AS
    BEGIN
    	RETURN @value;
    END;
    GO
    DECLARE @T AS TABLE (ProductID int PRIMARY KEY);
     
    INSERT @T (ProductID)
    SELECT p.ProductID
    FROM AdventureWorks2012.Production.Product AS p;

    T-SQLスカラー関数に関するこの問題は、すべての更新クエリに影響します– INSERTUPDATEDELETE 、およびMERGE 。不要なHalloweenProtectionが常に追加のEagerTableSpoolとして表示されるとは限らず、スカラー関数呼び出しがビューや計算された列定義などに隠されている可能性があるため、この問題がいつ発生するかを知ることはより困難になります。

    [パート1|パート2|パート3|パート4]


    1. Android:Sqliteエラー-(1)nullに近い:構文エラー

    2. PerfToolsのRACインスタンスを覚えておいてください

    3. Oracleでの外部キー作成の問題

    4. SQL ServerのWHERE句のアンパサンド(&)演算子