すべてのプロダクト
Search
ドキュメントセンター

ApsaraDB RDS:RDS SQL Server におけるメモリプレッシャーの診断と最適化

最終更新日:Jan 20, 2026

ご利用の RDS SQL Server インスタンスのメモリ使用量がしきい値を超え、パフォーマンスが低下した場合、メモリプレッシャーを特定し、解決する必要があります。CPU や I/O のボトルネックとは異なり、メモリの問題が最初に現れるとは限りません。しかし、重要なシナリオでは、メモリプレッシャーが他の問題を引き起こしたり、悪化させたりすることがあります。例えば、キャッシュが不足するとディスク I/O が増加し、メモリ競合によってクエリが待機状態になります。このトピックでは、メモリプレッシャーについて説明し、主要なメトリックの表示方法、診断手順、一般的な例、およびさまざまなシナリオに応じた最適化の提案を解説します。

背景情報

SQL Server は動的なメモリ管理メカニズムを使用しています。高コストなディスク I/O のオーバーヘッドを削減するため、SQL Server はデフォルトで、利用可能なメモリをできるだけ多く使用して、バッファープールにデータを、プランキャッシュに実行計画をキャッシュします。内部または外部のメモリプレッシャーを検出した場合にのみ、キャッシュのトリミングや解放を行います。

  • 高いメモリ使用量は正常です: SQL Server は、オペレーティングシステムが低メモリ状態を通知しない限り、メモリを解放しません。したがって、SQL Server の高いメモリ使用量は通常、正常な状態です。これは、インスタンスがパフォーマンス向上のためにリソースを効率的に使用していることを示します。

  • メモリプレッシャー: メモリプレッシャーは、現在のワークロードに対してメモリが不足している場合にのみ発生します。これにより、データページが頻繁にスワップされ、メモリ割り当ての待機やエラーが発生します。

RDS SQL Server では、メモリプレッシャーは通常、次の 4 つの形式で現れます。

  • バッファープールのプレッシャー (データキャッシュの不足)

    データキャッシュのスペースが不足すると、データページは長時間メモリ内に留まることができず、ページ生存期間 (PLE) が継続的に低下します。これによりキャッシュヒット率が低下し、システムは高コストなディスク I/O を通じてデータを取得せざるを得なくなります。

  • クエリワークスペースのプレッシャー (計算用メモリの不足)

    ソートやハッシュを伴う複雑なクエリは、クエリメモリ許可と呼ばれる別のメモリ割り当てを要求します。利用可能なメモリが不足している場合、クエリはキューに入れられて待機するか、データを tempdb にスピルします。これにより、実行効率が大幅に低下します。

  • 非バッファープールのプレッシャー (内部コンポーネントの競合)

    データのキャッシュに加えて、SQL Server は実行計画、ロック管理、接続のためにもメモリを必要とします。これは Stolen メモリと呼ばれます。これらのコンポーネントが過剰なメモリを使用すると、バッファープールで利用可能なスペースが減少し、メモリプレッシャーが発生します。

  • 外部メモリプレッシャー (SQL Server 以外からのメモリプレッシャー)

    これは、ホストのオペレーティングシステムの利用可能なメモリが非常に少なくなり、SQL Server がメモリを解放せざるを得なくなった場合に発生します。RDS はオペレーティングシステムのメモリを保護するためにパラメーターを自動的に構成します。しかし、リソース割り当てのバランスが取れていないシナリオ、例えば、4 コア 8 GB のインスタンスで 2 TB のデータを扱うような、少量のメモリインスタンスが大量のデータをサポートする場合に、この問題が発生することがあります。物理バックアップコンポーネントがストリーミングバックアップを開始すると、バックアップによる追加のメモリオーバーヘッドがメモリプレッシャーを強めます。これにより、SQL Server はキャッシュを再利用せざるを得なくなり、パフォーマンスの変動やメモリ不足エラーを引き起こす可能性があります。

問題を診断するには、まずパフォーマンスメトリックを使用してメモリプレッシャーの種類を特定します。その後、適切な最適化策を適用できます。

1. メモリ使用量の特定

1.1 モニタリングとアラートを使用したメモリプレッシャーの初期判断

操作手順

  1. RDS インスタンスリストに移動し、上部でリージョンを選択してから、対象のインスタンス ID をクリックします。

  2. 左側のナビゲーションウィンドウで、[モニタリングとアラート] をクリックします。

  3. メモリ関連のメトリックである mem_usagePage_life_expectancybufferpool_hit_ratio を表示します。

メモリプレッシャーの初期確認

RDS SQL Server では、mem_usage (メモリ使用量) が 90% を超えるのは通常、正常な状態です。SQL Server は、データキャッシュなどのタスクのために、利用可能なメモリをできるだけ多く使用してパフォーマンスを向上させます。このメトリックには、SQL Server プロセス、オペレーティングシステム、および管理サービスによって使用されるメモリも含まれます。

ただし、Linked Server、CLR、In-Memory OLTP、または多数の XEvents や Traces など、メモリオーバーヘッドの高い機能が有効になっているインスタンスでは、実際のメモリ消費量が構成された max server memory の値を大幅に超える可能性があります。これにより、全体的なメモリプレッシャーが増加し、パフォーマンス変動のリスクが高まります。この状況では、max server memory の値を下げて、システムのために十分なメモリを確保し、安定性を確保する必要があります。

mem_usage メトリックの推奨上限値:

  • 512 GB メモリインスタンス:97%

  • 256 GB インスタンスのメモリ使用率:96%

  • 192 GB 以下のインスタンスタイプ:95%

1.2 Autonomy Service を使用したメモリ使用量の詳細分析

説明

Performance Insight 機能は現在、一部のリージョンおよび特定のバージョンでのみ利用可能です。クラウドディスクを使用する RDS for SQL Server 2008 R2 インスタンスではサポートされていません。ご利用のインスタンスがサポート対象のリージョンにあり、Autonomy Service が有効になっていることを確認してください。

操作手順

  1. RDS インスタンスリストに移動し、上部でリージョンを選択してから、対象のインスタンス ID をクリックします。

  2. 左側のナビゲーションウィンドウで、[Autonomy Service] > [パフォーマンス最適化] を選択し、[Performance Insight] タブを開きます。

  3. ページの右上隅にある [カスタムメトリック] をクリックします。リストから目的のメモリ関連のパフォーマンスメトリックを選択し、[確認] をクリックします。一般的なメモリメトリックは、[メモリ使用量の分類][AdvancedMemUsage] (高度なメモリ使用量の分類) の 2 つのグループに整理されています。

メモリ使用量の分類メトリック

[メモリ使用量の分類] のメトリックは、一般的に SQL Server Memory Manager オブジェクトのパフォーマンスカウンターに対応しています。

  • Total_Server_Memory_Kb:このメトリックを使用して、メモリが上限に達しているかどうかを大まかに判断します。

  • Database_Cache_Memory_KbStolen_Server_Memory_Kb:これらのメトリックを使用して、データキャッシュと他の用途に割り当てられたメモリの比率を判断します。

  • Free_Memory_Kb:このメトリックを使用して、短期的に利用可能なメモリを観察します。

ただし、単一のメモリ使用量メトリックの値が高いか低いかだけでは、それが良いか悪いかを判断するには不十分です。パフォーマンスの傾向とワークロードも考慮する必要があります。

メトリック名

メトリックの説明

典型的な異常シナリオ

診断と最適化の提案

Total_Server_Memory_Kb

現在のコミット済みサーバーメモリ

SQL Server メモリマネージャーがオペレーティングシステムから要求し、コミットした合計メモリ。この値は通常、ワークロードとともに増加し、max server memory の上限に近づきます。

/

/

Stolen_Server_Memory_Kb

非データページメモリ (Stolen)

他の内部コンポーネントが使用するためにバッファープールから「借用」された合計メモリ。これには、実行計画キャッシュ、ロック構造、接続コンテキスト、補助データ構造が含まれます。

内部コンポーネントの競合

この値が常に合計メモリの高い割合 (40% 以上など) を占める場合。これにより、データキャッシュで利用可能なスペースが減少し、頻繁なデータページのスワップや I/O のジッターが発生します。

Stolen メモリの増加は、最も一般的な内部メモリプレッシャーの原因の 1 つです。AdvancedMemUsage の詳細メトリックを使用して、プランキャッシュまたは他のコンポーネントがメモリを消費しているかどうかを特定し、対象を絞った最適化を適用します。

Database_Cache_Memory_Kb

データページキャッシュメモリ

データベースのデータページをキャッシュするためにバッファープールで使用されるメモリ。この値は、クエリデータのヒット率に直接影響します。

キャッシュスペースの不足

値が急激に低下するか、激しく変動し、ディスク読み取り (Page Reads) が急増します。この減少は通常、Stolen メモリの増加によって引き起こされます。

これは受動的なメトリックです:

  • データキャッシュが不足している場合は、まずメモリを消費しているコンポーネントに対処します。例えば、SQL を最適化してプランキャッシュを削減します。

  • すべてのコンポーネントが正常に動作している場合、ワーキングセットがインスタンスタイプの容量よりも大きいことを示します。メモリ構成のアップグレードを検討してください。

SQL_Cache_Memory_Kb

合計実行計画キャッシュ

SQL リクエスト、ストアドプロシージャ、トリガーなど、さまざまな実行計画を格納するために使用される合計メモリ。これは Stolen メモリの主要なコンポーネントです。

プランキャッシュの肥大化

値が継続的に増加し、大量のメモリを消費しますが、キャッシュヒット率は低いです。これは、パラメーター化されていない多くのアドホック SQL クエリが、単一使用プランでキャッシュを埋め尽くす場合に一般的です。

  • `optimize for ad hoc workloads` オプションを有効にします。

  • アプリケーション側で SQL のパラメーター化を強制し、リテラル値のみが異なる重複した SQL クエリを削減します。

  • Object-Relational Mapping (ORM) フレームワークによって自動生成されるクエリが多すぎないか確認します。

Optimizer_Memory_Kb

クエリ最適化メモリ

実行計画のコンパイルおよび生成中にクエリオプティマイザーによって消費される動的メモリ。

コンパイルストーム

値が一貫して高いままであるか、密なスパイクを示します。これは通常、システムが大規模な同時コンパイル、いわゆるコンパイルストームを経験していることを示します。

このメトリックを `SQL Compilations/sec` とともに監視します。両方が同時に増加する場合、スキーマの変更、統計の更新、またはキャッシュプレッシャーが頻繁なプランの再コンパイルを引き起こしているかどうかを確認します。

Lock_Memory_Kb

ロック構造メモリ

行ロック、ページロック、テーブルロックなどのデータベースロックとその状態情報を維持するためのメモリオーバーヘッド。

ロックリソースの過剰使用

値が数百メガバイト以上に急増します。これは通常、コミットされていない大規模なトランザクションが大量の行ロックを保持しているか、ロックエスカレーションによる深刻なブロッキングが原因です。

  • 非常に大規模なトランザクションは避けてください。バッチでの削除または更新操作は、より小さなチャンクに分割します。

  • インデックス設計を最適化してスキャン範囲を減らし、それによってロックの数を減らします。

  • ロックが解放されるのを妨げている長時間実行トランザクションがないか確認します。

Connection_Memory_Kb

接続コンテキストメモリ

クライアントとデータベースサーバー間の物理的な接続の状態を維持するためのメモリオーバーヘッド。

接続リークまたはストーム

短期間での接続数の急増により、このメモリメトリックが急速に上昇します。これは、アプリケーションが接続プールを正しく使用していないか、接続を適切に閉じていない場合に一般的です。

  • アプリケーション側で接続プールを構成して接続を再利用します。

  • 接続プールに適切な最大サイズを設定します。

  • アプリケーションコードに、接続を開いた後に閉じない (Close/Dispose) などの論理エラーがないか確認します。

Free_Memory_Kb

空きメモリ

SQL Server 内でコミットされているが、現在どの特定のリクエストにも割り当てられていないメモリの量。

リソースの枯渇 (コンテキストが必要)

値が長時間 0 または 0 に近いままであり、ページ生存期間 (PLE) が同時に減少します。

  • Free Memory が 0 に近いのは正常です。SQL Server は利用可能なすべてのメモリをキャッシュに使用する傾向があります。

  • メモリ不足の問題は、Free Memory が 0 であり、多くの PAGEIOLATCH 待機がある場合にのみ確認されます。これが発生した場合は、スケールアウトまたはワークロードの最適化を検討してください。

AdvancedMemUsage メトリック (Stolen メモリの内訳)

Stolen メモリの使用量をさらに診断するために、RDS は [AdvancedMemUsage] 詳細メトリックを提供します。これは、Stolen メモリを異なるメモリ割り当てカテゴリに分類します。これらのメトリックは、SQL Server の特定の内部メモリコンポーネントに対応し、メモリ使用量の分類の詳細な内訳を提供します。

メトリック名

意味

典型的な異常シナリオ

診断と最適化の提案

CACHESTORE_SQLCP_KB

アドホッククエリプランキャッシュ

アドホッククエリ、準備済みステートメント、サーバーサイドカーソルの実行計画を格納するために使用されます。

パラメーター化されていないクエリが多すぎる

アプリケーションが、論理的には同じだがリテラルテキストが異なる SQL 文 (連結されたパラメーターなど) を頻繁に送信します。これにより、キャッシュ内に単一使用の実行計画が蓄積され、メモリの肥大化を引き起こします。

  • アプリケーションコードを変更して、パラメーター化クエリを使用します。

  • 単一使用プランのメモリオーバーヘッドを削減するために、`optimize for ad hoc workloads` インスタンスパラメーターを有効にします。

CACHESTORE_OBJCP_KB

オブジェクトプランキャッシュ (OBJCP)

ストアドプロシージャ、関数、トリガーのコンパイル済み実行計画をキャッシュするために使用されます。

過剰なプランの冗長性

  • データベースにストアドプロシージャが多すぎます。

  • パラメーター スニッフィングの問題により、同じストアドプロシージャが異なるパラメーターに対して複数の実行計画バージョンを生成し、保持します。

  • 未使用のデータベースオブジェクトを監査し、クリーンアップします。

  • パラメーター スニッフィングによるプランの肥大化に対処するには、`OPTION(RECOMPILE)` またはステートメントレベルのクエリヒントを使用します。

  • 最適化のために SQL Server 2022 の Parameter Sensitive Plan (PSP) 機能を使用します。

CACHESTORE_PHDR_KB

解析および代数化ツリーキャッシュ

クエリのコンパイル中に SQL テキストに対応する解析ツリーまたは代数化ツリーを一時的に格納するために使用されます。

過度に複雑な SQL テキスト

例えば、クエリ文に非常に大きな定数リスト (数千の値を持つ `IN` 句など) や、非常に長い SQL テキストが含まれている場合。これにより、コンパイル中に生成される解析構造が大量のメモリを占有します。

  • SQL 文に多くの定数をハードコーディングすることは避けてください。代わりに、一時テーブルまたはテーブル値パラメーター (TVP) を使用してパラメーターを渡します。

  • 複雑な SQL 文を簡素化するか、非常に大きなバッチ操作を分割します。

MEMORYCLERK_SOSNODE_KB

SQLOS ノードメモリ割り当て (SOSNODE)

スケジューリング、メモリ管理、その他のタスクに使用される内部 SQL Server 構造に割り当てられるメモリ。各 NUMA ノードには独自の SOSNODE メモリセットがあり、通常は安定しています。

通常の状況では、SOSNODE メモリはインスタンスの実行とともにわずかに増加し、その後安定します。しかし、低スペックの高可用性版 RDS SQL Server インスタンスでは、SOSNODE メモリがゆっくりと肥大化する問題が発生することがあります。これは、特に小規模でミラーリングアーキテクチャを持つ高可用性インスタンスで、断片化またはリークが原因であると疑われます。これは、`MEMORYCLERK_SOSNODE_KB` が時間とともに継続的に増加し、長期間減少しないという形で現れます。

このメモリは、内部の SQL Server スケジューリングに使用されます。小規模な高可用性インスタンスでは、ゆっくりとした増加が発生する可能性があります。詳細については、「付録 1:SOSNODE メモリの増加問題」をご参照ください。

MEMORYCLERK_SQLCLR_KB

CLR マネージドメモリ

SQL Server 統合共通言語ランタイム (CLR) コンポーネントが必要とするメモリリソースを割り当て、管理するために使用されます。

ユーザーによってデプロイされたカスタム CLR アセンブリ (C# で書かれたストアドプロシージャや関数など) が、複雑なメモリ操作を伴うか、適切に破棄されないオブジェクトを含んでいる可能性があります。

  • カスタム CLR コードをレビューし、アンマネージドリソースが適切に解放されていることを確認します。

  • 未使用の CLR アセンブリを削除します。

  • CLR の最大メモリ使用量を制限します。このオプションは CLR の実行が失敗する可能性があるため、注意して使用してください。

MEMORYCLERK_SQLSTORENG_KB

ストレージエンジン内部メモリ

tempdb の行バージョンストア、メタデータキャッシュ、ファイル管理構造など、コアストレージエンジンコンポーネントに使用されます。

行バージョンストアの蓄積

データベースでスナップショット分離または Read Committed Snapshot Isolation (RCSI) が有効になっている場合、長時間実行される未コミットのトランザクションがあると、tempdb のバージョンチェーンがクリーンアップできなくなります。これにより、この部分のメモリが消費されます。

  • 長時間実行される未コミットのトランザクションを監視し、終了させます。

  • `sys.dm_tran_version_store_space_usage` を確認して、バージョンストアの使用状況を確認します。

  • 高並行性環境で過度に長いトランザクションスパンを維持しないように、ビジネスロジックを標準化します。

USERSTORE_SCHEMAMGR_KB

スキーママネージャーユーザーストア (SCHEMAMGR)

テーブル定義などのデータベースオブジェクトのメタデータをキャッシュします。TempDB の一時テーブル、テーブル変数、一時プロシージャも、このキャッシュを使用してメタデータ記述を格納します。

  • 高使用量シナリオ:システム内に非常に多数のオブジェクトがある場合や、短期間に一時オブジェクトが頻繁に作成・破棄される場合 (TempDB での一時テーブルやテーブル変数の大規模な使用など) に、SCHEMAMGR メモリが増加します。

  • 影響経路: スキーマメタデータキャッシュの高い使用率は、一部の Stolen メモリを占有しますが、通常は主要なメモリコンシューマーではありません。極端なケース (TempDB でのテーブルの大量作成など) では、メモリの再利用が遅れる可能性があります。

  • 一時テーブルを頻繁に作成・破棄するのではなく、再利用します。

  • 未使用のオブジェクトをクリーンアップします。

  • 多くの永続オブジェクトを持つデータベースでは、不要なオブジェクトの蓄積を避けるために、定期的にスキーマ設計をレビューします。一般的に、この値の割合は高くなく、特別な対応は不要です。

`AdvancedMemUsage` メトリックを分析することで、どのコンポーネントがメモリを消費しているかを判断し、適切な対策を講じることができます。例:

  • `CACHESTORE_SQLCP` や `OBJCP` などのプランキャッシュ関連のメトリックが高い場合は、実行計画の再利用とパラメーター化の最適化に重点を置きます。

  • `MEMORYCLERK_SQLCLR` が異常な場合は、CLR モジュールを確認します。

  • `MemoryClerk_SOSNODE` が長期間にわたって上昇する場合は、SQL Server エンジンの問題を示します。再起動などの運用措置を検討してください。

2. メモリプレッシャーの分析と最適化

メモリプレッシャーが存在することを確認した後、その種類を分析し、特定のビジネスシナリオに基づいて最適化を適用します。以下のセクションでは、一般的なシナリオの診断アプローチと最適化方法について説明します。

シナリオ 1:バッファープールキャッシュの不足

異常な動作

インスタンスが以下の特徴を示す場合、通常はデータページが長時間メモリに留まることができず、バッファープールにプレッシャーがかかっていることを意味します。

  • I/O メトリックの急増: Performance Insight で `Page_Reads` (物理読み取り) が大幅に増加し、ディスク I/O スループットが増加します。

  • 短いライフサイクル: ページ生存期間 (PLE) が一貫して健全なしきい値を下回っています。

PLE は、データページがバッファープールに留まる平均時間 (秒) です。従来の「300 秒」というしきい値は、大容量メモリのインスタンスにはもはや適用できません。メモリ仕様に基づいてしきい値を動的に計算します。一般的な数式は次のとおりです。

PLE = (バッファープールメモリ (GB) / 4 GB) × 300

PLE 計算例:

  • 16 GB インスタンスタイプ (バッファープール約 12 GB):妥当な PLE は 900 秒 (12 / 4 × 300) より大きい必要があります。

  • 128 GB インスタンスタイプ (バッファープール約 110 GB):妥当な PLE は 8250 秒 (110 / 4 × 300) より大きい必要があります。

診断アプローチ

  • 相関関係の確認: PLE の低下が `Page_Reads` の同時増加を伴うかどうかを確認します。それらが強く相関し、ビジネスのピーク時に発生する場合、キャッシュ不足と診断できます。

  • 干渉の除外: PLE が低下しても I/O が大幅に増加しない場合、またはバックアップ、インデックスの再構築、CheckPoint 中に発生する場合は、通常は正常な変動です。短期的な低下は過度に注意する必要はありません。

  • 容量の評価: `Database_Cache_Memory_Kb` をアクティブなデータセットのサイズと比較します。キャッシュがいっぱいでも、ホットデータの非常に小さな部分しか保持できない場合は、容量のボトルネックを示します。

最適化ソリューション

  • メモリのスケールアップ: ワーキングセットが現在のメモリ仕様よりもはるかに大きいことを確認した場合、インスタンスタイプをアップグレードすることが、I/O レイテンシを削減する最も直接的な方法です。

  • クエリの最適化: フルテーブルスキャンや大規模な集約がないか確認します。存在する場合は、インデックスを追加してスキャンをシークに変更します。これにより、大規模なクエリがバッファープールからすべてのホットデータをフラッシュするのを防ぎます。

  • ワーキングセットの削減:

    • アーカイブ: 既存のコールドデータをアーカイブテーブルに移行します。

    • 圧縮: テーブルまたはインデックスのデータ圧縮を有効にして、同じ量のメモリにより多くのデータページを収容します。

    • メンテナンス: 断片化が激しいインデックスを再構築してページ密度を向上させます。未使用のインデックスを削除してメンテナンスのオーバーヘッドを削減します。

シナリオ 2:プランキャッシュの肥大化 (高いプランキャッシュ使用率)

異常な動作

インスタンス内の Stolen メモリの割合が異常に増加し、エンジンの上限に達し、バッファープールのスペースを圧迫し始めると、通常は実行計画が効果的に再利用されていないことを意味します。

  • メモリ特性: `Stolen_Server_Memory_Kb` が継続的に増加し、高いレベルで推移し、プランキャッシュの内部クォータ上限に近づきます。これにより、`Database_Cache_Memory_Kb` が十分なメモリを受け取れなくなり、PLE が低下します。

  • パフォーマンス特性: キャッシュが頻繁にクリアされ、新しいプランが再コンパイルされるため、CPU 使用率の増加を伴います。また、データページキャッシュのスペースが減少するため、ディスク I/O の増加も伴います。

  • 典型的なトリガーシナリオ: アプリケーションがパラメーター化されていない多くのアドホック SQL 文を送信するか、ORM フレームワークが大量の単一使用実行計画を生成します。これにより、キャッシュが価値の低いプランで埋め尽くされます。

診断アプローチ

  • 相関関係の確認: Stolen メモリが過去のベースラインよりも大幅に高く、PLE の低下と負の相関関係を示しているかどうかを観察します。

  • 単一使用プランの割合: usecounts = 1 のプランの数とそのメモリ使用量です。単一使用プランがプランキャッシュの非常に高い割合 (たとえば、50% を超える) を占める場合、大量のメモリが浪費されていることを示します。

    診断用の SQL コマンド

    WITH PlanStats AS
    (
        SELECT
            cp.usecounts,
            cp.size_in_bytes / 1024.0 / 1024.0 AS size_mb
        FROM sys.dm_exec_cached_plans AS cp
        WHERE cp.cacheobjtype = 'Compiled Plan'
    )
    SELECT
        total_plans           = COUNT(*),
        single_use_plans      = SUM(CASE WHEN usecounts = 1 THEN 1 ELSE 0 END),
        single_use_ratio_pct  = 
            CAST(
                100.0 * SUM(CASE WHEN usecounts = 1 THEN 1 ELSE 0 END) / NULLIF(COUNT(*), 0)
                AS DECIMAL(5,2)
            ),
        total_plan_mb         = CAST(SUM(size_mb) AS DECIMAL(18,2)),
        single_use_plan_mb    = CAST(SUM(CASE WHEN usecounts = 1 THEN size_mb ELSE 0 END) AS DECIMAL(18,2)),
        single_use_mem_pct    = 
            CAST(
                100.0 * SUM(CASE WHEN usecounts = 1 THEN size_mb ELSE 0 END) 
                / NULLIF(SUM(size_mb), 0)
                AS DECIMAL(5,2)
            )
    FROM PlanStats;
    
  • ソースの特定: `sys.dm_exec_sql_text` を使用して単一使用プランの SQL テキストをサンプリングします。同じ構造でリテラル値が異なる多くの SQL 文が見つかった場合、原因がアプリケーションのパラメーター化不使用であることを確認できます。次の SQL 文は、最も多くの実行計画キャッシュスペースを使用する上位 20 の単一使用キャッシュを示しています。

    キャッシュを表示する SQL コマンド

    -- 最も多くのメモリを消費するアドホッククエリとそのテキストの上位 20 件を表示
    SELECT TOP 20
        cp.usecounts AS [Execution_Count],
        cp.size_in_bytes / 1024 AS [Plan_Size_KB],
        cp.objtype AS [Object_Type],
        st.text AS [SQL_Text] 
    FROM sys.dm_exec_cached_plans AS cp
    CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
    WHERE cp.objtype = 'Adhoc' 
      AND cp.usecounts = 1 
    ORDER BY cp.size_in_bytes DESC;

最適化ソリューション

  • Optimize for Ad hoc Workloads オプションの有効化

    • 手順: RDS インスタンスリストページに移動し、インスタンスの詳細ページに移動します。左側のナビゲーションウィンドウで、[パラメーター設定] をクリックします。optimize for ad hoc workloads パラメーターを検索し、有効にします。

    • 原則: このパラメーターを有効にすると、アドホック SQL 文の最初の実行では軽量なスタブのみがキャッシュされます。完全なプランは 2 回目の実行でのみ保存されます。

    • 効果: これにより、単一使用クエリの Stolen メモリ使用量が大幅に削減され、メモリプレッシャーが緩和されます。このオプションは CPU のコンパイルオーバーヘッドを削減するものではなく、メモリ使用量のみを最適化します。

  • アプリケーション側の最適化

    • パラメーター化クエリ: 文字列連結の代わりにパラメーター化クエリを使用するようにコードロジックを変更します。

    • ORM の構成: ORM フレームワークで自動パラメーター化またはセカンドレベルキャッシュが有効になっているかどうかを確認します。

  • 強制パラメーター化の有効化

    アプリケーションコードを変更できない場合は、RDS コンソールでデータベースレベルの強制パラメーター化を有効にできます。

    • RDS インスタンスリストに移動し、インスタンスの詳細ページを開き、左側のナビゲーションウィンドウで [データベース管理] をクリックします。

    • 強制パラメーター化が有効になっているデータベースの場合、右側の [詳細の表示] をクリックします。

    • [基本情報] セクションで、parameterization パラメーターを FORCED に設定し、[送信] をクリックします。

      説明

      このパラメーターを有効にした後、プランの変更によって一部の複雑なクエリでパフォーマンスリグレッションが発生する可能性があるため、ステージング環境で検証してください。

シナリオ 3:クエリメモリ許可の不足

異常な動作

バッファープールのプレッシャーとは異なり、このタイプのプレッシャーは必ずしも PLE の低下を引き起こしません。クエリにソートまたはハッシュ操作が含まれる場合、十分なワークスペースメモリを取得できないと、2 つの典型的な症状が現れます。

  • キューイングと待機 (Wait)

    • 動作: クエリは実行を開始せず、SUSPENDED 状態のままになります。

    • メトリック: 待機タイプは RESOURCE_SEMAPHORE (非常に深刻で、メモリが枯渇しクエリがキューイングしていることを示す) または RESOURCE_SEMAPHORE_QUERY_COMPILE です。`Memory Grants Pending` パフォーマンスカウンターの値が急増します。

  • ディスクへのスピル (Spill)

    • 動作: クエリは実行中ですが、非常に遅いです。

    • メトリック: TempDB への I/O 書き込みが大幅に増加します。

    • 実行計画: Sort または Hash Match オペレーターに「Operator used tempdb to spill data...」というメッセージとともに黄色の警告アイコンが表示されます。これは、メモリが不足し、SQL Server が計算のために中間結果をディスクに書き込むことを余儀なくされたことを示します。

診断アプローチ

リアルタイムまたは履歴データをクエリすることで、メモリ許可の消費が最も高い SQL 文を分析できます。結果の実行計画 (`query_plan`) で、Spill キーワードを検索するか、Sort または `Hash` オペレーターに警告アイコンがあるかどうかを確認します。

  • 現在、リアルタイムで最も多くのメモリ許可を消費しているステートメントを見つけます。

    リアルタイムクエリの分析

    -- リアルタイムのメモリ許可を表示
    SELECT
        mg.session_id,
        mg.request_time,
        mg.grant_time, -- NULL の場合、クエリはメモリを待機中 (RESOURCE_SEMAPHORE)
        (mg.requested_memory_kb / 1024.0) AS requested_mb,
        (mg.granted_memory_kb / 1024.0) AS granted_mb,
        (mg.required_memory_kb / 1024.0) AS required_mb,
        mg.queue_id,
        mg.wait_order,
        st.text AS sql_text,
        qp.query_plan
    FROM sys.dm_exec_query_memory_grants AS mg
    CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) AS st
    CROSS APPLY sys.dm_exec_query_plan(mg.plan_handle) AS qp
    ORDER BY mg.granted_memory_kb DESC;
    
  • キャッシュ内の履歴実行統計を分析して、履歴的に最も多くのメモリを要求したクエリを見つけます。

    履歴クエリの分析

    SELECT TOP 20
        qs.execution_count,
        (qs.max_grant_kb / 1024.0) AS max_grant_mb, -- 最大の単一実行で要求されたメモリ
        (qs.total_grant_kb / qs.execution_count / 1024.0) AS avg_grant_mb,
        (qs.total_worker_time / qs.execution_count / 1000.0) AS avg_cpu_ms,
        qs.last_execution_time,
        SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
            ((CASE qs.statement_end_offset
              WHEN -1 THEN DATALENGTH(st.text)
              ELSE qs.statement_end_offset
              END - qs.statement_start_offset)/2) + 1) AS query_text,
        qp.query_plan
    FROM sys.dm_exec_query_stats AS qs
    CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
    CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
    ORDER BY qs.max_grant_kb DESC;
    

主な原因と最適化ソリューション

  • 古い統計 (最も一般的な原因)

    • 原因: 古い統計により、オプティマイザーが結果セットの行数を著しく過小評価します。例えば、実際には 100 万行を処理しているのに 1 行と見積もるかもしれません。これに基づき、オプティマイザーは 1 MB などの非常に少量のメモリを要求します。これにより、メモリが不足するため、実行中に頻繁にディスクへのスピルが発生します。

    • ソリューション: データベースの統計を定期的に、またはオフピーク時に更新して、オプティマイザーがメモリ要件を正確に見積もれるようにします。

  • 大規模クエリによる同時競合

    • 原因: フルテーブルソートや大規模なハッシュ集計を必要とする複数の重いレポートクエリが同時に実行されます。これによりメモリが枯渇し、後続のクエリが待機キューに入ることがあります。

    • ソリューション: 実行をずらす戦略を実装します。メモリを大量に消費するレポートタスクを夜間に順次実行するようにスケジュールし、ピーク時のリソース競合を回避します。

  • コードまたはインデックス設計の問題

    • メモリ消費の多いオペレーター: クエリに複雑な JOIN、GROUP BY、ORDER BY、または DISTINCT 操作が含まれているが、適切なインデックスがありません。これにより、SQL Server はメモリ内に巨大なハッシュテーブルを構築したり、中間ソートを実行したりすることを余儀なくされ、メモリ需要が急増します。

    • 過剰なデータ幅: SELECT * を使用したり、不要な長いテキスト列を選択したりすると、単一のデータ行が「肥大化」します。メモリ許可サイズ = 推定行数 × 平均行幅 であるため、列が広いほど、必要な合計メモリも多くなります。

    • ソリューション:

      • ソート済みインデックスの作成: カバーリングインデックスまたは順序付きインデックスを作成して、オプティマイザーがメモリ消費の少ない物理操作を選択できるようにします。

      • アプリケーション側の最適化: 「必要十分」の原則に厳密に従います。必要な列のみを選択して、データ行の幅を減らします。多くの結合を持つ大規模なクエリの場合は、複数のより単純なステップに分割してみてください。

3. まとめ:メモリプレッシャー診断ワークフロー

第 1 段階:初期評価 (PLE と待機状態に基づく)

まず、ページ生存期間 (PLE) とページ読み取りメトリックを確認します。通常、3 つの状況があります。

  1. メモリプレッシャーなし: PLE が大きな変動なく一貫してしきい値を上回っている場合、メモリ使用率が 100% に近くても、これは SQL Server の正常な動作です。この場合、メモリは無視し、CPU または I/O のボトルネックのトラブルシューティングに集中します。

  2. バッファープールのプレッシャー: PLE が頻繁にしきい値を下回るか、急激に低下し、1 秒あたりのページ読み取り数が多い (Page Reads が増加する) 場合、データページが長時間メモリに留まることができないことを意味します。次のステップに進み、メモリ構成を分析します。

  3. クエリメモリプレッシャー: PLE は正常に見えるが、システムでメモリ許可待機 (`Memory Grants Pending > 0`) が発生したり、クエリが頻繁に TempDB にスピルしたりする場合、これはクエリ実行メモリの不足の問題を示します。これはバッファープールとは無関係です。関連する SQL 文を直接最適化します。例えば、ソートを減らしたり、インデックスを追加したりします。

第 2 段階:メモリ構成の分析 (データベースキャッシュ vs. Stolen)

プレッシャーが存在することを確認した後、メモリ使用量の分類を表示して、他のコンポーネントによってメモリが圧迫されているのか、全体的にメモリが不足しているのかを区別します。

  • 高い Stolen の割合: `Database_Cache_Memory_Kb` が小さく、`Stolen_Server_Memory_Kb` が大きな割合 (例えば 20% 以上) を占めている場合、大量のメモリがプランキャッシュ、接続、CLR などの非データページ目的に使用されていることを意味します。これは典型的な割り当ての問題です。誤用されているメモリを回収することに重点を置くべきです。

  • データベースキャッシュがいっぱい: `Database_Cache_Memory_Kb` がすでにメモリの大部分を占め、Stolen の割合が非常に低く、`Free_Memory_Kb` が一貫して 0 に近い場合、これは物理メモリが現在のホットデータ量を処理できないことを示します。これは容量のボトルネックです。最適化によってホットデータの使用量を減らすか、スケールアップによってメモリ制限を増やすことができます。

第 3 段階:消費箇所の特定と総合的な判断

最初の 2 段階の分析に基づいて、パフォーマンス最適化を行うか、メモリのスケールアップを行うかを決定します。

  • パフォーマンス最適化 (高い Stolen またはクエリメモリプレッシャーの場合)

    • 高消費の Stolen コンポーネントの特定: `sys.dm_os_memory_clerks` ビューをクエリします。`CACHESTORE_SQLCP` の使用率が高い場合、プランキャッシュの肥大化を示します。SQL 文を最適化する必要があります。例えば、パラメーター化を使用します。`SQLCLR` または他のコンポーネントが異常な場合は、それに応じてアプリケーションロジックを調整します。

    • クエリメモリ問題の解決: メモリを過剰に要求するクエリについては、SQL 文を書き換えたり、統計を更新したり、インデックスを追加したりして、メモリ使用量を削減します。スケールアップでは、通常、この種の競合問題は解決できません。

  • メモリのスケールアップ (データベースキャッシュ容量不足の場合)

    メモリが主にデータページ (DB キャッシュ) の格納に使用されており、明らかな高い Stolen メモリがないにもかかわらず、PLE が依然として低いことを確認した場合、これはビジネスのホットデータ量が現在の物理メモリ容量を超えていることを意味します。この場合、インスタンスタイプをアップグレードするか、メモリを増やすことが、I/O ボトルネックを解決し、パフォーマンスを向上させる最も効果的な方法です。

付録 1:SOSNODE メモリの増加問題

RDS SQL Server 高可用性版インスタンスでは、インスタンスが数ヶ月間再起動せずに長時間実行されると、`MEMORYCLERK_SOSNODE_KB` がゆっくりと上昇傾向を示すことがあります。この傾向はビジネスのピークとは直接関係なく、特に 2 コア 4 GB や 4 コア 8 GB などの小規模インスタンスで顕著です。このメモリは Max Server Memory に計上され、バッファープールのスペースを減少させるため、インスタンスに明確なメモリボトルネックが現れます。

これは SOSNODE オブジェクトの典型的なメモリリークです。Microsoft はこれを完全に修正しておらず、このリスクは複数の SQL Server バージョンに存在します。以下の対応を推奨します。

  • リスクを軽減するために、より高いバージョンにアップグレードします。

  • メモリリークの傾向に基づき、オフピーク時に 2〜4 ヶ月ごとに計画的な再起動をスケジュールします。これにより、リークしたメモリが解放され、長期間の蓄積によるメモリ枯渇を防ぎます。Errorlog に 701 エラーが表示されることがあります。

RDS もこのような異常な増加傾向を継続的に追跡しています。メトリックがしきい値に達すると、自動的に積極的な O&M タスクがトリガーされます。コンソールのイベントセンターで対応するタスクを表示できます。