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

SQL Serverの内部:PlanCachingPt。 I –プランの再利用

    SQL Serverは約30年以上前から存在しており、私はSQLServerをほぼ同じくらい長い間使用してきました。私はこの素晴らしい製品の何年にもわたって(そして何十年にもわたって!)そしてバージョンの多くの変化を見てきました。これらの投稿では、SQL Serverの機能や側面のいくつかを、時には歴史的な視点とともにどのように見ているかを紹介します。

    問題のあるオペレーターに関するKalenの最近のブログをここでチェックしてください。

    SQLサーバー診断の計画は、クエリオプティマイザーが送信された合法的なクエリの適切な計画を見つけることができなければならないため、作成に費用がかかる可能性があります。オプティマイザは、複数の結合順序、複数のインデックスを評価します。クエリと関連するテーブルに応じて、さまざまなタイプの結合およびグループ化アルゴリズム。同じクエリを再実行すると、SQL Serverは既存のプランを再利用することで、多くのリソースを節約できます。ただし、既存の計画を再利用できるとは限りません。また、再利用することが常に良いとは限りません。次の2つの記事では、プランが再利用されるタイミングと再コンパイルされるタイミングについて説明します。

    最初に、プランのさまざまなフレーバーと、プランキャッシュにあるものを確認するために最も頻繁に使用するメタデータビューを確認します。私は、私が最も頻繁に役立つと思う情報を提供する自分自身の見解を書きました。 SQL Serverは6種類のクエリプランをキャッシュしますが、プランキャッシュの調整に通常使用されるのは2つだけです。これらは、COMPILEDPLANとCOMPILEDPLANSTUBです。私のビューでは、これら2種類のキャッシュオブジェクトを除くすべてが除外されます。 COMPILED PLANSには、AD HOC、PREPARED、PROCの3種類があります。 3種類すべてについてコメントします。

    COMPILED PLANSだけを見ている場合でも、SQL Server自体によって生成されるため、通常は無視する必要のある多くのプランがキャッシュにあります。これには、ファイルストリームまたは全文検索インデックス、またはインメモリOLTPで機能する内部クエリを探す計画が含まれます。そのため、私のビューでは、関心のないほとんどの計画を試行して除外するためのフィルターが追加されています。このビューを作成するための sp_cacheobjectsというスクリプトをダウンロードできます。 、ここから。

    ビューが使用するすべてのフィルターを使用しても、SQLServer独自の内部クエリの一部がキャッシュに残っています。この領域でテストを行うときは、通常、プランのキャッシュを頻繁にクリアします。キャッシュからすべてのプランをクリアする最も簡単な方法は、コマンドDBCCFREEPROCCACHEを使用することです。

    アドホックコンパイル済みプラン

    最も単純なタイプのプランはアドホックです。これは、別のカテゴリに当てはまらない基本的なクエリに使用されます。スクリプトをダウンロードしてsp_cacheobjectsビューを作成した場合は、次のコマンドを実行できます。 AdventureWorksデータベースのどのバージョンでも機能するはずです。このスクリプトは、テーブルのコピーを作成し、その上にいくつかの一意のインデックスを作成します。また、小数をマッサージして小数桁を削除します。

    USE AdventureWorks2016;
    GO
    DROP TABLE IF EXISTS newsales;
    GO
    -- Make a copy of the Sales.SalesOrderHeader table
    SELECT * INTO dbo.newsales
    FROM Sales.SalesOrderHeader;
    GO
    UPDATE dbo.newsales
    SET SubTotal = cast(cast(SubTotal as int) as money);
    GO
    CREATE UNIQUE index newsales_ident
        ON newsales(SalesOrderID);
    GO
    CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
    GO
    -- Adhoc query plan reuse
    DBCC FREEPROCCACHE;
    GO
    -- adhoc query
    SELECT * FROM dbo.newsales
    WHERE SubTotal = 4;
    GO
    SELECT * FROM sp_cacheobjects;
    GO

    私の出力では、2つのアドホックプランが表示されます。 1つは、 newsalesのSELECTステートメント用です。 テーブル、もう1つは私の sp_cacheobjectsからのSELECT用です 見る。プランはキャッシュされているため、まったく同じクエリを再度実行すると、同じプランを再利用でき、使用回数が表示されます。 値が増加します。ただし、落とし穴があります。アドホックプランを再利用するには、SQL文字列が完全に同じである必要があります。 SQLの文字を変更すると、クエリは同じクエリとして認識されず、新しいプランが生成されます。スペースを追加したり、コメントを含めたり、改行したりしても、同じ文字列ではありません。大文字と小文字を変更すると、ASCIIコード値が異なるため、同じ文字列ではなくなります。

    newsales の最初のSELECTステートメントのさまざまなバリエーションを実行して、これを自分で試すことができます。 テーブル。それぞれのキャッシュに異なる行が表示されます。検索した番号の変更、大文字と小文字の変更、コメントの追加、改行など、いくつかのバリエーションを実行した後、キャッシュに次のように表示されます。私の見解からのSELECTは再利用されていますが、他のすべてには使用回数があります 値1。

    アドホッククエリプランを再利用するための追加要件の1つは、クエリを実行しているセッションで同じSETオプションが有効になっている必要があることです。 。出力には、SETOPTSと呼ばれるクエリテキストの右側に表示される別の列があります。これは、関連する各SETオプションのビットを含むビット文字列です。たとえば、SET ANSI_NULLS OFFなどのオプションのいずれかを変更すると、ビット文字列が変更され、元のビット文字列と同じプランを再利用できなくなります。

    作成されたコンパイル済みプラン

    キャッシュされたコンパイル済みプランの2番目のタイプは、PREPAREDプランです。クエリが特定の要件を満たしている場合。実際には自動的にパラメータ化できます。メタデータにPREPAREDとして表示され、SQL文字列にパラメータマーカーが表示されます。次に例を示します。

    PREPAREDプランでは、パラメーターマーカーが@ 1として表示され、実際の値は含まれていません。実際の値が5555のADHOCクエリの行がありますが、これは実際には実際のクエリの「シェル」にすぎないことに注意してください。プラン全体をキャッシュするのではなく、クエリといくつかの識別の詳細のみをキャッシュして、クエリプロセッサがキャッシュ内のパラメータ化されたプランを見つけられるようにします。サイズに注意してください(使用されたページ )PREPAREDプランよりもはるかに小さいです。

    SIMPLEパラメーター化と呼ばれるデフォルトのパラメーター化モードは、どのプランをパラメーター化できるかについて非常に厳密です。デフォルトでパラメータ化できるのは、実際には最も単純なクエリだけです。 JOIN、GROUP BY、OR、および他の多くの比較的一般的なクエリ構造を含むクエリは、クエリがパラメータ化されるのを防ぎます。これらの構造がないことに加えて、SIMPLEパラメーター化で最も重要なことは、クエリが安全であることです。これは、パラメータに渡される値に関係なく、可能なプランは1つだけであることを意味します。 (もちろん、パラメーターのないクエリも安全である可能性があります。)私のクエリは列 SalesOrderIDで完全に一致するものを探しています。 、一意のインデックスがあります。したがって、既存の非クラスター化インデックスを使用して、一致する行を見つけることができます。 55555など、どのような値を使用しても、複数の行が存在することはありません。これは、計画が引き続き良好であることを意味します。

    アドホッククエリプランの例では、 SubTotalに一致する値を探していました。 。いくつかの小計 値は数回発生するか、まったく発生しないため、非クラスター化インデックスが適しています。ただし、他の値が何度も発生する可能性があるため、インデックスは役に立ちません。したがって、クエリプランは安全ではなく、クエリをパラメータ化することはできません。そのため、最初の例でアドホックプランを確認しました。

    JOINまたはその他の許可されていない構造を使用したクエリがある場合は、データベースオプションを変更することで、SQLServerにパラメーター化をより積極的に行うように指示できます。

    ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
    GO

    データベースをFORCEDパラメーター化に設定すると、SQL Serverは、JOIN、GROUP BY、ORなどのクエリを含め、さらに多くのクエリをパラメーター化できます。ただし、SQLServerがSAFEではないクエリをパラメーター化できることも意味します。少数の行のみが返される場合に適したプランを考え出し、その後、多数の行が返される場合にプランを再利用する場合があります。これにより、パフォーマンスが非常に最適化されなくなる可能性があります。

    準備された計画の最後のオプションの1つは、明示的に計画を準備する場合です。この動作は通常、 SQLPrepareを使用するアプリケーションを介して呼び出されます。 およびSQLExecute API。パラメータマーキングを使用してクエリとは何かを指定し、データ型を指定し、使用する特定の値を指定します。その後、同じクエリを異なる特定の値で再度実行でき、既存のプランが使用されます。明示的に準備されたプランを使用することは、SQL Serverがパラメーター化されておらず、希望する場合に使用できますが、SQLServerが後続のパラメーターに適さないプランを使用することを妨げることはありません。さまざまな入力値を使用してクエリをテストし、プランを再利用した場合に期待されるパフォーマンスが得られることを確認する必要があります。

    メタデータ(例:私の sp_cacheobjects ビュー)は、3つのタイプのプランすべてに対してPREPAREDを示しています:FORCEDおよびSIMPLE自動パラメーター化とEXPLICITパラメーター化。

    Procコンパイル済みプラン

    最後のobjtype コンパイル済みプランの値は、Procとして表示されるストアドプロシージャの値です。可能であれば、サーバー自体からの管理が容易なため、再利用可能なコードにはストアドプロシージャが最適ですが、常に最高のパフォーマンスが得られるとは限りません。 FORCEDパラメーター化オプション(および明示的なパラメーター化)を使用するのと同じように、ストアドプロシージャは「パラメータースニッフィング」を使用します。これは、渡された最初のパラメーター値が計画を決定することを意味します。後続の実行が同じプランで正常に実行される場合、パラメータースニッフィングは問題ではなく、再コンパイルと再最適化のコストを節約できるため、実際に有益です。ただし、異なる値を使用した後続の実行で元の計画を使用すべきでない場合は、問題が発生します。問題を引き起こすパラメータスニッフィングの例を紹介します

    newsalesに基づいてストアドプロシージャを作成します 以前に使用したテーブル。プロシージャには、 SalesOrderIDに基づいてフィルタリングする単一のクエリが含まれます。 非クラスター化インデックスを作成した列。クエリは不等式に基づいているため、一部の値の場合、クエリは数行を返し、インデックスを使用できます。その他の値の場合、クエリは多数の行を返すことができます。つまり、クエリは安全ではありません。

    USE AdventureWorks2016;
    GO
    DROP PROC IF EXISTS get_sales_range;
    GO
    CREATE PROC get_sales_range
       @num int
    AS
        SELECT * FROM dbo.newsales
        WHERE SalesOrderID < @num;
    GO

    オプションSETSTATISTICSIO ONを使用して、プロシージャの実行時に実行されている作業量を確認します。まず、数行のみを返すパラメータを使用して実行します。

    SET STATISTICS IO ON
    GO
    EXEC get_sales_range 43700;
    GO

    STATISTICS IO値は、41行を返すのに43回の論理読み取りが必要であることを報告しています。これは、クラスター化されていないインデックスでは正常です。ここで、はるかに大きな値を使用してプロシージャを再度実行します。

    EXEC get_sales_range 66666;
    GO
    SELECT * FROM sp_cacheobjects;
    GO
    This time, we see that SQL Server used a whole lot more reads:

    実際、ニュースのテーブルスキャン テーブルは843回の読み取りしか必要としないため、これはテーブルスキャンよりもはるかにパフォーマンスが低下します。 sp_cacheobjects ビューは、PROCプランがこの2回目の実行に再利用されたことを示しています。これは、パラメータスニッフィングが適切でない場合の例です。

    では、パラメータスニッフィングが問題になる場合はどうすればよいでしょうか。次の投稿では、SQL Serverが新しい計画を立て、古い計画を再利用しない時期について説明します。再コンパイルを強制(または奨励)する方法と、SQLServerがクエリを自動的に再コンパイルするタイミングについても説明します。

    Spotlight Cloudは、パフォーマンスの監視とSQLサーバーの診断に革命をもたらすことができます。以下のリンクを使用して、無料トライアルを開始してください:


    1. データベース環境を保護するためのSQLServerロックダウンについて

    2. SQLServerで数値を小数点以下2桁にフォーマットする4つの関数

    3. RailsとPostgreSQL:ロールpostgresは存在しません

    4. SQLでNULLがNULLと一致しないのはなぜですか?