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

SQLServerの一時オブジェクトのキャッシュ

    テーブルの作成は、比較的リソースと時間のかかる操作です。サーバーは、新しいデータとインデックス構造のストレージスペースを見つけて割り当て、複数のシステムメタデータテーブルに対応するエントリを作成する必要があります。このすべての作業は、高い同時実行性の下で常に正しく機能し、リレーショナルデータベースに期待されるすべてのACID保証を満たす方法で実行する必要があります。

    SQL Serverでは、これは、データベースへの物理的な変更の前に、詳細なトランザクションログエントリが永続ストレージに安全にコミットされるようにしながら、正しい種類のロックとラッチを正しい順序で取得することを意味します。これらのログエントリにより、トランザクションのロールバックまたはシステムクラッシュが発生した場合に、システムがデータベースを一貫した状態に戻すことができます。

    テーブルの削除も同様にコストのかかる操作です。幸いなことに、ほとんどのデータベースは、頻繁にテーブルを作成または削除しません。これに対する明らかな例外は、システムデータベース tempdb です。 。この単一のデータベースには、SQL Serverインスタンス全体にわたるすべての一時テーブルとテーブル変数の物理ストレージ、割り当て構造、システムメタデータ、およびトランザクションログエントリが含まれています。

    一時テーブルとテーブル変数の性質上、他のデータベースオブジェクトタイプよりもはるかに頻繁に作成および削除されます。この自然に高い頻度の作成と破棄が、単一のデータベースに関連付けられているすべての一時テーブルとテーブル変数の集中効果と組み合わされた場合、tempdb<の割り当てとメタデータ構造で競合が発生する可能性があることは驚くことではありません。 / em> データベース。

    一時オブジェクトのキャッシュ

    tempdbへの影響を減らすため 構造では、SQLServerは再利用のために一時オブジェクトをキャッシュできます。 SQL Serverは、一時オブジェクトを削除する代わりに、システムメタデータを保持し、テーブルデータを切り捨てます。テーブルが8MB以下の場合、切り捨ては同期的に実行されます。それ以外の場合は、遅延ドロップが使用されます。いずれの場合も、切り捨てにより、ストレージ要件が単一の(空の)データページに削減され、割り当て情報が単一のIAMページに削減されます。

    キャッシングにより、次回一時オブジェクトを作成するための割り当てとメタデータのコストのほとんどすべてが回避されます。 tempdbへの変更を少なくすることの副作用として データベースを完全なドロップと再作成のサイクルよりも、一時オブジェクトのキャッシュにより、必要なトランザクションログの量も削減されます。

    キャッシングの実現

    テーブル変数とローカル一時テーブルはどちらもキャッシュできます。キャッシングの資格を得るには、ローカルの一時テーブルまたはテーブル変数が必要です。 モジュールで作成する:

    • ストアドプロシージャ(一時的なストアドプロシージャを含む)
    • トリガー
    • マルチステートメントテーブル値関数
    • スカラーユーザー定義関数

    マルチステートメントのテーブル値関数の戻り値はテーブル変数であり、それ自体がキャッシュされる場合があります。テーブル値パラメーター(テーブル変数でもあります)は、パラメーターがクライアントアプリケーションから送信されるときにキャッシュできます。たとえば、SqlDbType.Structuredを使用する.NETコードでキャッシュできます。 。ステートメントがパラメーター化されている場合、テーブル値のパラメーター構造はSQLServer2012以降でのみキャッシュできます。

    次のできません キャッシュされる:

    • グローバル一時テーブル
    • アドホックSQLを使用して作成されたオブジェクト
    • 動的SQLを使用して作成されたオブジェクト(例:EXECUTEを使用) またはsys.sp_executesql

    キャッシュするには、一時オブジェクトを追加でしてはいけません

    • 名前付き制約があります(明示的な名前のない制約は完全に問題ありません)
    • オブジェクトの作成後に「DDL」を実行します
    • WITH RECOMPILEを使用して定義されたモジュールに参加する オプション
    • WITH RECOMPILEを使用して呼び出されます EXECUTEのオプション ステートメント

    いくつかの一般的な誤解に明示的に対処するには:

    • TRUNCATE TABLE しません キャッシングを防ぐ
    • DROP TABLE しません キャッシングを防ぐ
    • UPDATE STATISTICS しません キャッシングを防ぐ
    • 統計の自動作成はしません キャッシングを防ぐ
    • 手動CREATE STATISTICS します キャッシュを防ぐ

    モジュール内のすべての一時オブジェクトは、キャッシュの適合性について個別に評価されます。キャッシュできない1つ以上の一時オブジェクトを含むモジュールは、同じモジュール内の他の一時オブジェクトのキャッシュの対象となる可能性があります。

    一時テーブルのキャッシュを無効にする一般的なパターンは、最初のテーブル作成ステートメントの後にインデックスを作成することです。ほとんどの場合、これは主キーと一意の制約を使用して回避できます。 SQL Server 2014以降では、INDEXを使用して、テーブル作成ステートメントに一意でない非クラスター化インデックスを直接追加するオプションがあります。 条項。

    監視と保守

    キャッシュカウンターDMVを使用して、現在キャッシュされている一時オブジェクトの数を確認できます。

     SELECT DOMCC。[type]、DOMCC.pages_kb、DOMCC.pages_in_use_kb、DOMCC.entries_count、DOMCC.entries_in_use_countFROM sys.dm_os_memory_cache_counters AS DOMCCWHEREDOMCC。[name]=N'Temporary Tables&Table Variables'; 

    結果の例は次のとおりです。

    キャッシュエントリは使用中と見なされます 含まれているモジュールのいずれかの部分が実行されている限り。同じモジュールを同時に実行すると、複数のキャッシュされた一時オブジェクトが作成されます。同じモジュールの複数の実行計画(おそらくセッションSETが異なるため オプション)は、同じモジュールの複数のキャッシュエントリにもつながります。

    キャッシュエントリは、メモリに対する競合するニーズに応じて、時間の経過とともに期限切れになる可能性があります。キャッシュされた一時オブジェクトは、親モジュールの実行プランがプランキャッシュから削除されたときに、(同時に、バックグラウンドシステムスレッドによって)削除することもできます。

    本番システムではサポートされていません(または推奨されていません)が、一時オブジェクトキャッシュストアは、テスト目的で手動で完全にクリアできます。

     DBCC FREESYSTEMCACHE('Temporary Tables&Table Variables')WITH MARK_IN_USE_FOR_REMOVAL; WAITFOR DELAY '00:00:05'; 

    5秒の遅延により、バックグラウンドクリーンアップタスクを実行する時間ができます。このコマンドは実際には危険ですことに注意してください 。排他的にアクセスできるテストインスタンスでのみ(自己責任で)使用する必要があります。テストが終了したら、SQLServerインスタンスを再起動します。

    キャッシュ実装の詳細

    テーブル変数は、 tempdbの「実際の」ユーザーテーブルによって実装されます。 データベース(テーブルではありませんが、直接クエリできます)。関連するテーブルの名前は「#」で、その後にオブジェクトIDの8桁の16進表現が続きます。次のクエリは関係を示しています:

    -テーブル変数DECLARE@ZASテーブル(z整数NULL); -対応するsys.tablesentrySELECTT. [name]、ObjIDFromName =CONVERT(integer、CONVERT(binary(4)、RIGHT(T. [name]、8)、2))、T. [object_id]、T. [ type_desc]、T.create_date、T.modify_dateFROM tempdb.sys.tables AS T WHERE T. [name] LIKE N'#[0-9A-F] [0-9A-F] [0-9A-F] [0 -9A-F] [0-9A-F] [0-9A-F] [0-9A-F] [0-9A-F]'; 

    結果の例を以下に示します。オブジェクト名から計算されたオブジェクトIDが実際のオブジェクトIDとどのように一致するかに注意してください。

    そのスクリプトをアドホックSQLとして実行すると、異なる tempdbが生成されます。 各実行時のオブジェクトID(およびオブジェクト名)(キャッシュなし)。モジュール(ストアドプロシージャなど)内に同じスクリプトを配置すると、テーブル変数をキャッシュできるようになり(動的SQLが使用されていない限り)、オブジェクトIDと名前は実行ごとに同じになります。

    >

    テーブル変数がキャッシュされていない場合、基になるテーブルが作成され、毎回削除されます。一時オブジェクトのキャッシュが有効になっている場合、テーブルはドロップされるのではなく、モジュールの最後で切り捨てられます。 変更なし テーブル変数がキャッシュされるときにシステムメタデータに。割り当て構造とトランザクションログへの影響は、テーブル内の行を削除し、モジュールの終了時に余分なデータと割り当てページを削除することに限定されます。

    一時テーブル

    テーブル変数の代わりに一時テーブルを使用する場合、基本的なメカニズムは基本的に同じですが、名前の変更手順が2つ追加されます。一時テーブルがキャッシュされない場合 、 tempdbに表示されます おなじみのユーザー指定の名前に続いて、一連のアンダースコアと、最後のサフィックスとしてのオブジェクトIDの16進表現が続きます。ローカル一時テーブルは、明示的に削除されるか、作成されたスコープが終了するまで残ります。アドホックSQLの場合、これはセッションがサーバーから切断されたときを意味します。

    キャッシュされた一時テーブルの場合 、モジュールを初めて実行すると、キャッシュされていない場合と同様に一時テーブルが作成されます。モジュールの最後で、(作成されたスコープが終了すると)自動的に削除されるのではなく、一時テーブルが切り捨てられてから名前が変更されます オブジェクトIDの16進表現(テーブル変数で見られるとおり)。次回モジュールを実行すると、キャッシュされたテーブルの名前が16進形式からユーザー指定の名前(アンダースコアと16進オブジェクトID)に変更されます。

    モジュールの開始時と終了時の追加の名前変更操作には、少数のシステムメタデータの変更が含まれます。 。したがって、キャッシュされた一時テーブルは、非常に高い再利用率の下で、少なくともある程度のメタデータの競合が発生する可能性があります。それでも、キャッシュされた一時テーブルのメタデータへの影響は、キャッシュされていない場合よりもはるかに低くなります(毎回テーブルを作成および削除します)。

    一時オブジェクトのキャッシュがどのように機能するかの詳細と例は、以前の記事にあります。

    キャッシュされた一時テーブルの統計

    前述のように、統計は自動的に 一時オブジェクトのキャッシュの利点を失うことなく一時テーブルに作成されます(注意として、統計を手動で作成すると キャッシュを無効にします。

    重要な注意点は、統計 キャッシュされた一時テーブルに関連付けられているリセットされない モジュールの最後にオブジェクトがキャッシュされたとき、またはモジュールの開始時にキャッシュされたオブジェクトがキャッシュから取得されたとき。結果として、キャッシュされた一時テーブルの統計は、無関係の以前の実行から残される可能性があります。つまり、統計にはまったく関係がない可能性があります。 一時テーブルの現在の内容に移動します。

    テーブル変数よりもローカル一時テーブルを優先する主な理由が正確な分布統計の可用性であることを考えると、これは明らかに望ましくありません。緩和策では、(if)基になるキャッシュされたオブジェクトへの変更の累積数が内部再コンパイルしきい値に達すると、統計が自動的に更新されます。詳細が複雑で直感に反するため、これを事前に評価することは困難です。

    一時オブジェクトキャッシュの利点を維持しながら、最も包括的な回避策は次のとおりです。

    • 手動でUPDATE STATISTICS モジュール内の一時テーブル。 および
    • OPTION (RECOMPILE)を追加します 一時テーブルを参照するステートメントへのヒント

    当然、これを行うにはコストがかかりますが、これはほとんどの場合許容できます。実際、最初にローカル一時テーブルを使用することを選択することにより、モジュールの作成者は、プランの選択は一時テーブルの内容に敏感である可能性が高いと暗黙的に言っているので、再コンパイルは理にかなっています。統計を手動で更新すると、再コンパイル中に使用される統計がテーブルの現在の内容を反映するようになります(確かに予想されます)。

    これがどのように機能するかについての詳細は、このトピックに関する以前の記事を参照してください。

    概要と推奨事項

    モジュール内の一時オブジェクトキャッシングにより、 tempdbの共有割り当てとメタデータ構造へのプレッシャーを大幅に軽減できます。 データベース。これらの一時オブジェクトのキャッシュと再利用にはメタデータの変更がまったく含まれないため(名前変更操作なし)、テーブル変数を使用するときに最大の削減が発生します。実行時にすべてのテーブル変数のデータを保持するには、単一のキャッシュされたデータページでは不十分な場合でも、割り当て構造に関する競合が発生する可能性があります。

    テーブル変数のカーディナリティ情報が不足していることによるプランの品質への影響は、OPTION (RECOMPILE)を使用することで軽減できます。 またはトレースフラグ2453(SQL Server 2012以降で使用可能)。これらの緩和策は、テーブル内の行の総数に関するオプティマイザー情報のみを提供することに注意してください。

    一般化するには、テーブル変数 データが小さい場合(理想的には単一のデータページに収まり、競合のメリットを最大化する場合)、およびプランの選択がテーブル変数に存在する値に依存しない場合に最適です。

    データ配布に関する情報の場合 (密度とヒストグラム)はプランの選択に重要です。ローカルの一時テーブルを使用してください 代わりは。一時テーブルキャッシュの条件を満たすことを確認してください。これは、ほとんどの場合、最初のテーブル作成ステートメントの後にインデックスまたは統計を作成しないことを意味します。これは、INDEXの導入により、SQLServer2014以降でより便利になりました。 CREATE TABLEの句 声明。

    明示的なUPDATE STATISTICS データが一時テーブルにロードされた後、OPTION (RECOMPILE) モジュール内にキャッシュされた一時テーブルの期待されるすべての利点を生み出すには、テーブルを参照するステートメントに関するヒントが必要になる場合があります。

    一時的なオブジェクトは、プランの品質の観点から、明確なメリットが得られる場合にのみ使用することが重要です。一時オブジェクトの過度の、非効率的な、または不必要な使用は、 tempdbにつながる可能性があります 一時的なオブジェクトのキャッシュが達成された場合でも、競合。

    最適な一時オブジェクトのキャッシュでは、 tempdbを減らすのに十分でない場合があります 一時的なオブジェクトが完全に正当化された場合にのみ使用される場合でも、すべての場合で許容レベルまでの競合。インメモリテーブル変数または非耐久性インメモリテーブルを使用すると、そのような場合にターゲットを絞ったソリューションを提供できますが、常にトレードオフが必要であり、現在、すべての場合に最適なオプションを表す単一のソリューションはありません。


    1. 最新のPostgreSQLトレンド:最も時間のかかるタスクと追跡する重要なメトリック

    2. SQLServerでテーブル値関数を作成する

    3. PostgreSQLのクラウドバックアップオプション

    4. T-SQLスタッフコマンド