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

単純なパラメータ化と簡単な計画—パート1

    これは、単純なパラメータ化に関するシリーズの最初の部分です。 および簡単な計画 。これらの2つのコンパイル機能は密接に関連しており、同様の目標があります。単純なステートメントを頻繁に送信するワークロードの目標パフォーマンスと効率の両方。

    「単純な」名前と「慣用名」の名前にもかかわらず、どちらも微妙な動作と実装の詳細があり、それらがどのように機能するかを理解するのが難しくなる可能性があります。このシリーズは、基本についてはあまり詳しく説明していませんが、最も経験豊富なデータベースの専門家でさえもつまずく可能性のあるあまり知られていない側面に焦点を当てています。

    この最初のパートでは、簡単な紹介の後、単純なパラメーター化の効果を見ていきます。 プランキャッシュ上。

    単純なパラメータ化

    ほとんどの場合、明示的にパラメータ化することをお勧めします。 サーバーに依存してそれを行うのではなく、ステートメント。明示的であると、パラメーターが使用される場所、使用される正確なデータ型、プランが再利用されるタイミングなど、パラメーター化プロセスのすべての側面を完全に制御できます。

    ほとんどのクライアントとドライバーは、明示的なパラメーター化を使用する特定の方法を提供します。 sp_executesqlのようなオプションもあります 、ストアドプロシージャ、および関数。

    パラメータスニッフィングやSQLインジェクションの関連する問題については説明しません。これは、重要ではありますが、これらはこのシリーズの焦点では​​ないためです。それでも、両方を頭の前に置いてコードを書く必要があります。

    簡単に変更できないレガシーアプリケーションやその他のサードパーティコードの場合、明示的なパラメータ化が常に可能であるとは限りません。テンプレートプランガイドを使用すると、いくつかの障害を克服できる場合があります。いずれにせよ、サーバー側に少なくともいくつかのパラメーター化されたステートメントが含まれていないのは、異常なワークロードになります。

    シェルプラン

    SQLServer2005が強制パラメータ化を導入したとき 、既存の自動パラメータ化 機能の名前がSimpleParameterizationに変更されました 。用語の変更にもかかわらず、単純なパラメータ化 自動パラメータ化と同じように機能します 常に実行:SQL Serverは、アドホックステートメントの定数リテラル値をパラメーターマーカーに置き換えようとします。目的は、キャッシュされたプランの再利用を増やすことでコンパイルを減らすことです。

    SQL Server 2019CU14でStackOverflow2010データベースを使用した例を見てみましょう。データベースの互換性は150に設定され、並列処理のコストしきい値は50に設定されて、現時点では並列処理を回避します。

    EXECUTE sys.sp_configure
        @configname = 'show advanced options',
        @configvalue = 1;
    RECONFIGURE;
    GO
    EXECUTE sys.sp_configure
        @configname = 'cost threshold for parallelism',
        @configvalue = 50;
    RECONFIGURE;

    コード例:

    -- Clear the cache of plans for this database
    ALTER DATABASE SCOPED CONFIGURATION 
        CLEAR PROCEDURE_CACHE;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 2521;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 2827;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 3144;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 3151;
    GO

    これらのステートメントは、定数リテラル値のみが異なる述語を特徴としています。 SQLServerは単純なパラメーター化を正常に適用します 、パラメータ化された計画になります。プランキャッシュをクエリするとわかるように、単一のパラメータ化されたプランが4回使用されます。

    SELECT
        CP.usecounts,
        CP.cacheobjtype,
        CP.objtype,
        CP.size_in_bytes,
        ST.[text],
        QP.query_plan
    FROM sys.dm_exec_cached_plans AS CP
    OUTER APPLY sys.dm_exec_sql_text (CP.plan_handle) AS ST
    OUTER APPLY sys.dm_exec_query_plan (CP.plan_handle) AS QP
    WHERE 
        ST.[text] NOT LIKE '%dm_exec_cached_plans%'
        AND ST.[text] LIKE '%DisplayName%Users%'
    ORDER BY 
        CP.usecounts ASC;

    結果はアドホックを示しています 元のステートメントごとにキャッシュエントリを計画し、1つの準備済み 計画:

    4つのアドホックプランと1つの準備済みプラン

    準備済み ステートメントはストアドプロシージャに似ていますが、 Adhocにあるリテラル値からパラメータが推測されます。 声明。サーバー側のパラメーター化プロセスについて考えるとき、これは有用なメンタルモデルとして言及します。

    SQLServerが両方をキャッシュすることに注意してください 元のテキストとパラメーター化されたフォーム。単純なパラメータ化が成功すると、元のテキストに関連付けられたプランはアドホックになります。 完全な実行計画は含まれていません。代わりに、キャッシュされたプランはシェル 準備済みへのポインタ以外はほとんどありません パラメータ化された計画。

    シェルプランのXML表現 次のようなテキストが含まれています:

    <ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.539" Build="15.0.4188.2">
    <BatchSequence>
    <Batch>
    <Statements>
    <StmtSimple 
      StatementText="SELECT U.DisplayName&#xD;&#xA;FROM dbo.Users AS U &#xD;&#xA;WHERE U.Reputation = 3151"
      StatementId="1" 
      StatementCompId="1" 
      StatementType="SELECT" 
      RetrievedFromCache="true" 
      ParameterizedPlanHandle="0x0600050090C8321CE04B4B079E01000001000000000000000000000000000000000000000000000000000000" 
      ParameterizedText="(@1 smallint)SELECT [U].[DisplayName] FROM [dbo].[Users] [U] WHERE [U].[Reputation]=@1" />
    </Statements>
    </Batch>
    </BatchSequence>
    </ShowPlanXML>

    それが全体の計画です。 ParameterizedPlanHandle アドホックからのポイント 完全なパラメータ化された計画へのシェル。ハンドル値は、4つのシェルプランすべてで同じです。

    スタブを計画する

    シェルプランは、完全にコンパイルされたプランよりも小さく、例では40KBではなく16KBです。単純なパラメーター化または多くの異なるパラメーター値を使用するステートメントが多数ある場合、これは依然としてかなりの量のメモリーを追加する可能性があります。ほとんどのSQLServerインスタンスは、このように無駄にする余裕があるほどメモリで溢れているわけではありません。シェルプランはSQLServerによって非常に使い捨てであると見なされますが、それらを見つけて削除するとリソースが消費され、競合のポイントになる可能性があります。

    アドホックワークロードの最適化オプションを有効にすることで、シェルプランの総メモリ消費量を削減できます。

    EXECUTE sys.sp_configure
        @configname = 'show advanced options',
        @configvalue = 1;
    RECONFIGURE;
    GO
    EXECUTE sys.sp_configure
        @configname = 'optimize for ad hoc workloads',
        @configvalue = 1;
    RECONFIGURE;

    これにより、シェルの代わりにアドホックステートメントが最初に検出されたときに小さなスタブがキャッシュされます。スタブはブックマークとして機能するため、サーバーは以前に正確なステートメントテキストが表示されたことを記憶できます。同じテキストに2回目に遭遇すると、コンパイルとキャッシュは、アドホックワークロードに最適化するかのように進行します。 有効にされていませんでした。

    アドホックワークロード用に最適化を使用して例を再実行する 有効にすると、プランキャッシュへの影響が表示されます。

    コンパイル済みプランスタブ

    アドホックステートメントのプランはキャッシュされず、スタブのみがキャッシュされます。 ParameterizedPlanHandleはありません 準備済みへのポインタ 計画。ただし、完全なパラメータ化された計画は キャッシュされました。

    (プランキャッシュをクリアせずに)テストバッチを2回実行すると、アドホックワークロードを最適化する場合と同じ結果が得られます。 有効にされていません—4つのアドホック 準備済みを指すシェルプラン 計画。

    続行する前に、アドホックワークロード用に最適化をリセットしてください ゼロに設定:

    EXECUTE sys.sp_configure
        @configname = 'optimize for ad hoc workloads',
        @configvalue = 0;
    RECONFIGURE;
    プランのキャッシュサイズ制限

    プランシェルまたはプランスタブのどちらを使用する場合でも、これらすべてのアドホックにはまだ欠点があります。 キャッシュエントリ。合計メモリ使用量についてはすでに説明しましたが、各プランキャッシュにも最大数があります エントリの。総メモリ使用量が問題にならない場合でも、膨大な量が問題になる可能性があります。

    文書化されたトレースフラグ174(エントリ数)およびトレースフラグ8032(合計サイズ)を使用して、制限を引き上げることができます。ワークロードやその他のメモリ需要によっては、これが最善の解決策ではない場合があります。結局のところ、それはより価値の低いアドホックをキャッシュすることを意味します 計画、他のニーズから記憶を奪う。

    準備されたプランのみをキャッシュする

    ワークロードが正確にでアドホックバッチを発行することがめったにない場合 同じステートメントテキスト、プランシェルまたはプランスタブのキャッシュはリソースの浪費です。これはメモリを消費し、SQLプランのときに競合を引き起こす可能性があります キャッシュストア(CACHESTORE_SQLCP )設定された制限内に収まるように縮小する必要があります。

    理想は、着信アドホックバッチをパラメータ化することですが、のみ パラメータ化されたバージョンをキャッシュします。パラメータ化されたキャッシュプランに一致させる前に、将来のアドホックステートメントをパラメータ化する必要があるため、これを行うにはコストがかかります。一方、これは、正確なをすでに述べているので、とにかく起こったでしょう。 対象のワークロードでテキストが一致することはまれです。

    Adhoc のキャッシングではなく、単純なパラメーター化の恩恵を受けるワークロードの場合 エントリには、いくつかのオプションがあります。

    文書化されていないトレースフラグ

    最初のオプションは、文書化されていないトレースフラグ253を有効にすることです。これにより防止 アドホックのキャッシュ 完全に計画します。時々提案されているように、それは単にそのような計画の数を制限したり、それらがキャッシュに「留まる」ことを妨げたりするものではありません。

    トレースフラグ253は、セッションレベルで有効にすることができ、その影響をその接続だけに制限するか、グローバルフラグまたは起動フラグとしてより広く有効にすることができます。これはクエリヒントとしても機能しますが、これらを使用すると、ここでは逆効果となる単純なパラメータ化が妨げられます。 Microsoftテクニカルペーパーの「SQLServer2012でのキャッシュと再コンパイルの計画」には、単純なパラメーター化を妨げるものの部分的なリストがあります。

    トレースフラグ253がアクティブなバッチがコンパイルされる前準備済みのみ ステートメントはキャッシュされます:

    ALTER DATABASE SCOPED CONFIGURATION 
        CLEAR PROCEDURE_CACHE;
    GO
    -- Do not cache ad-hoc plans
    DBCC TRACEON (253);
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 2521;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 2827;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 3144;
    GO
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 3151;
    GO
    -- Cache ad-hoc plans again
    DBCC TRACEOFF (253);
    GO

    プランキャッシュクエリは、準備済みのみを確認します ステートメントはキャッシュされて再利用されます。

    プリペアドステートメントのみがキャッシュされます

    キャッシュできないバッチ

    2番目のオプションは、バッチ全体をキャッシュ不可としてマークするステートメントを含めることです。 。適切なステートメントは、多くの場合、セキュリティ関連であるか、何らかの形で機密性があります。

    これは実用的ではないように聞こえるかもしれませんが、いくつかの緩和策があります。まず、機密性の高いステートメントを実行する必要はありません。存在する必要があります。 。その条件が満たされると、バッチを実行しているユーザーは許可も必要ありません。 機密ステートメントを実行します。注意してください、効果は機密ステートメントを含むバッチに限定されます。

    2つの適切に機密性の高いステートメントと使用例を以下に示します(テストステートメントは単一のバッチになっています):

    ALTER DATABASE SCOPED CONFIGURATION 
        CLEAR PROCEDURE_CACHE;
    GO
    -- Prevent caching of all statements in this batch.
    -- Neither KEY nor CERTIFICATE need to exist.
    -- No special permissions are needed.
    -- GOTO is used to ensure the statements are not executed.
    GOTO Start
        OPEN SYMMETRIC KEY Banana 
            DECRYPTION BY CERTIFICATE Banana;
    Start:
     
    /* Another way to achieve the same effect without GOTO
    IF 1 = 0
    BEGIN
        CREATE APPLICATION ROLE Banana 
        WITH PASSWORD = '';
    END;
    */
     
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 2521;
     
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 2827;
     
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 3144;
     
    SELECT U.DisplayName
    FROM dbo.Users AS U 
    WHERE U.Reputation = 3151;
    GO

    準備済み 単純なパラメータ化によって作成された計画 親バッチがキャッシュ不可としてマークされているにもかかわらず、キャッシュされて再利用されます。

    プリペアドステートメントのみがキャッシュされます

    どちらのソリューションも理想的ではありませんが、Microsoftがこの問題について文書化されサポートされているソリューションを提供するまでは、これらが私が知っている最良のオプションです。

    パート1の終わり

    このトピックについては、さらに多くの根拠があります。パート2では、単純なパラメータ化のときに割り当てられるデータ型について説明します。 採用されています。


    1. SQL Serverで1時間ごとにストアドプロシージャを実行するにはどうすればよいですか?

    2. ルート権限を取得されていないAndroidデバイスでデータベースを確認する方法

    3. 複数のエンティティバインディングを使用してElasticSearchインデックス構造を設定する方法

    4. SQLスクリプトを検証するためのコード