SQL Server のメモリ不足問題やメモリ不足のトラブルシューティング
現象
SQL Server では、複雑で豊富な機能セットに対応する複雑な メモリ アーキテクチャ を使用します。 メモリのニーズはさまざまであるため、多くのメモリ消費とメモリ不足が発生し、最終的にメモリ不足が発生する可能性があります。
SQL Server のメモリ不足を示す一般的なエラーがあります。 エラーの例を次に示します。
- 701: クエリを実行するのに十分なメモリを割り当てられません。
- 802: バッファー プール (データ ページまたはインデックス ページ) 内のページを割り当てるためのメモリの取得に失敗しました。
- 1204: ロックにメモリを割り当てられません。
- 6322: XML パーサーにメモリを割り当てられません。
- 6513:メモリ不足のため CLR を初期化できませんでした。
- 6533: メモリ不足のため、AppDomain がアンロードされました。
- 8318: メモリ不足のため、SQL パフォーマンス カウンターを読み込めませんでした。
- 8356 または 8359: メモリ不足のため、ETW または SQL トレースの実行に失敗します。
- 8556: メモリ不足のため MSDTC を読み込めませんでした。
- 8645: メモリ許可のメモリがないためにクエリを実行できない (並べ替えとハッシュ) 詳細については、「 SQL Server エラー 8645 のトラブルシューティング方法を参照してください。
- 8902: DBCC の実行中にメモリを割り当てられません。
- 9695 または 9696: Service Broker 操作にメモリを割り当てられません。
- 17131 または 17132: メモリ不足によるサーバーの起動エラー。
- 17890: SQL メモリが OS によってページングされるためにメモリを割り当てられません。
- 18053: 書式設定中にエラーが発生したため、エラーは簡潔なモードで出力されます。 トレース、ETW、通知などはスキップされます。
- 22986 または 22987: メモリ不足によるデータ キャプチャエラーを変更します。
- 25601: Xevent エンジンがメモリ不足です。
- 26053: メモリ不足のため、SQL ネットワーク インターフェイスの初期化に失敗します。
- 30085、30086、30094: メモリ不足のため、SQL フルテキスト操作が失敗します。
原因
多くの要因によってメモリが不足する可能性があります。 このような要因には、オペレーティング システムの設定、物理メモリの可用性、SQL Server 内のメモリを使用するコンポーネント、現在のワークロードのメモリ制限などがあります。 ほとんどの場合、メモリ不足エラーで失敗したクエリは、このエラーの原因ではありません。 全体として、原因は次の 3 つのカテゴリに分類できます。
原因 1: 外部または OS のメモリ不足
外部負荷とは、プロセス外のコンポーネントからのメモリ使用率が高いことを指します。これにより、SQL Server に必要なメモリが不足します。 システム上の他のアプリケーションがメモリを消費していて、メモリの可用性が低下しているかどうかを確認する必要があります。 SQL Server は、メモリ使用量を減らすことで OS のメモリ負荷に対応するように設計されたごく少数のアプリケーションの 1 つです。 つまり、アプリケーションまたはドライバーがメモリを要求した場合、OS はすべてのアプリケーションにメモリを解放するためのシグナルを送信し、SQL Server は独自のメモリ使用量を減らすことで応答します。 他のアプリケーションでは、その通知をリッスンするように設計されていないため、応答するアプリケーションはほとんどありません。 そのため、SQL Server がメモリ使用量の削減を開始すると、そのメモリ プールが減り、メモリを必要とするコンポーネントがメモリを取得できない可能性があります。 その結果、701 またはその他のメモリ関連のエラーが発生し始めます。 SQL がメモリを動的に割り当てて解放する方法の詳細については、「 SQL サーバー メモリ アーキテクチャを参照してください。 問題の詳細な診断と解決策については、この記事の「 External memory pressure 」を参照してください。
OS のメモリ不足を引き起こす可能性がある問題には、大きく分けて 3 つのカテゴリがあります。
- アプリケーション関連の問題: 1 つまたは複数のアプリケーションが一緒に使用可能な物理メモリを使い果たす。 OS は、メモリを解放しようとすることで、リソースに対する新しいアプリケーション要求に応答します。 一般的な方法は、メモリを使い果たすアプリケーションを見つけ、RAM 枯渇を引き起こすことなくメモリのバランスを取るために必要な手順を実行することです。
- デバイス ドライバーの問題: ドライバーがメモリ割り当て関数を誤って呼び出した場合、デバイス ドライバーによってすべてのプロセスのワーキング セット ページングが発生する可能性があります。
- オペレーション システムの製品に関する問題。
これらの手順とトラブルシューティングの手順の詳細については、 MSSQLSERVER_17890を参照してください。
原因 2: SQL Server からの内部メモリ不足
内部メモリ負荷とは、SQL Server プロセス内の要因が原因の低いメモリの可用性を指します。 SQL Server プロセス内で実行される可能性のある一部のコンポーネントは、SQL Server エンジンの "外部" です。 たとえば、リンク サーバーなどの OLE DB プロバイダー (DLL)、SQLCLR プロシージャまたは関数、拡張プロシージャ (XP)、OLE オートメーション (sp_OA*
) などがあります。 また、監視目的でプロセス内に Dll を挿入するウイルス対策やその他のセキュリティ プログラムなどがあります。 これらのコンポーネントのいずれかに問題があるか、設計が不適切な場合、メモリの消費量が多くなる可能性があります。 たとえば、リンク サーバーが外部ソースから SQL Server メモリに 2,000 万行のデータをキャッシュするとします。 SQL Server に関しては、メモリ クラークでメモリ使用量が多いことは報告されませんが、SQL Server プロセス内で消費されるメモリは多くなります。 たとえば、リンク サーバー DLL からのメモリの増加により、SQL Server はメモリ使用量の削減を開始し (上記を参照)、SQL Server 内のコンポーネントのメモリ条件が低くなり、メモリ不足エラーが発生します。 この問題に関する詳細な診断と解決策については、「 内部メモリの負荷(SQL Server からではなく)」を参照してください。
Note
SQL Server プロセス空間で使用されるいくつかの Microsoft DLL (たとえば、 MSOLEDBSQL、 SQL Native Client) は、レポートと割り当てのために SQL Server メモリ インフラストラクチャとインターフェイスできます。 select * from sys.dm_os_memory_clerks where type='MEMORYCLERK_HOST'
を実行してそれらの一覧を取得し、一部の割り当てに対するそのメモリ消費量を追跡できます。
原因 3: SQL Server コンポーネントからの内部メモリ不足
SQL Server エンジン内のコンポーネントから内部メモリ不足が発生すると、メモリ不足エラーが発生する可能性もあります。 SQL Server でメモリを割り当てる、 メモリ クラーク を介して追跡されるコンポーネントは数百個あります。 この問題を解決するには、最大のメモリ割り当てを担当するメモリ クラークを特定する必要があります。 たとえば、 OBJECTSTORE_LOCK_MANAGER
メモリ クラークに大量のメモリ割り当てが表示されている場合は、ロック マネージャーが大量のメモリを消費している理由を理解する必要があります。 多くのロックを取得するクエリがある場合があります。 これらのクエリを最適化するには、インデックスを使用するか、ロックを長時間保持するトランザクションを短縮するか、ロックのエスカレーションが無効になっているかどうかを確認します。 各メモリ クラークまたはコンポーネントには、メモリにアクセスして使用するための独自の方法があります。 詳細については、メモリ クラークの種類とその説明を参照してください。 この問題に関する詳細な診断と解決策については、「 SQL Server エンジンによる内部メモリ使用量を参照してください。
メモリ負荷の種類の視覚的表現
次のグラフは、SQL Server のメモリ不足の原因となる可能性がある負荷の種類を示しています。
トラブルシューティング データを収集するための診断ツール
次の診断ツールを使用して、トラブルシューティング データを収集できます。
パフォーマンス モニター
パフォーマンス モニターを使用して、次のカウンターを構成して収集します。
- Memory:Available MBytes
- Process:Working Set
- Process:Private Bytes
- SQL Server:Memory Manager: (すべてのカウンター)
- SQL Server: Buffer Manager: (すべてのカウンター)
DMV または DBCC MEMORYSTATUS
sys.dm_os_memory_clerksまたは DBCC MEMORYSTATUS を使用して、SQL Server 内の全体的なメモリ使用量を観察できます。
SSMS のメモリ消費量の標準レポート
SQL Server Management Studio でメモリ使用量を表示します。
- SQL Server Management Studio を起動し、サーバーに接続します。
- オブジェクト エクスプローラーで、SQL Server インスタンス名を右クリックします。
- コンテキスト メニューで、 Reports>Standard Reports>Memory Consumption を選択します。
PSSDiag または SQL LogScout
これらのデータ ポイントを自動的にキャプチャする別の方法は、 PSSDiag や SQL LogScout などのツールを使用することです。
PSSDiag を使用する場合は、 Perfmon コレクターと Custom Diagnostics\SQL Memory Error コレクターをキャプチャするように構成します。
SQL LogScout を使用する場合は、 Memory シナリオをキャプチャするように構成します。
次のセクションでは、各シナリオのより詳細な手順について説明します (外部または内部のメモリ不足)。
トラブルシューティングの手法
メモリ不足エラーが表示される場合や、しばらくの間、メモリ不足の問題が発生して解決される可能性があります。 そのような場合は、措置を講じる必要がある可能性があります。 ただし、エラーが複数の接続で複数回発生し、数秒以上持続する場合は、次のセクションの診断と解決策に従って、メモリ エラーのトラブルシューティングをさらに進めます。
外部メモリの負荷
SQL Server プロセスの外部でシステムのメモリ不足状態を診断するには、次の方法を使用します。
パフォーマンス モニター カウンターを収集します。 これらのカウンターを参照し、SQL Server 以外のアプリケーションまたはサービスにより、このサーバーでメモリが消費されているかどうかを調査します。
- Memory:Available MBytes
- Process:Working Set
- Process:Private Bytes
PowerShell を使用した Perfmon ログ収集の例を次に示します。
clear $serverName = $env:COMPUTERNAME $Counters = @( ("\\$serverName" +"\Memory\Available MBytes"), ("\\$serverName" +"\Process(*)\Working Set"), ("\\$serverName" +"\Process(*)\Private Bytes") ) Get-Counter -Counter $Counters -SampleInterval 2 -MaxSamples 1 | ForEach-Object { $_.CounterSamples | ForEach-Object { [pscustomobject]@{ TimeStamp = $_.TimeStamp Path = $_.Path Value = ([Math]::Round($_.CookedValue, 3)) } } }
システム イベント ログを確認し、メモリ関連のエラー (たとえば、仮想メモリ不足) を探します。
アプリケーション イベント ログで、アプリケーション関連のメモリの問題を確認します。
次に示すのは、PowerShell スクリプトの例で、キーワード "memory" についてシステム イベント ログとアプリケーション イベント ログに対してクエリを実行します。検索に "resource" などの他の文字列を自由に使用してください。
Get-EventLog System -ComputerName "$env:COMPUTERNAME" -Message "*memory*" Get-EventLog Application -ComputerName "$env:COMPUTERNAME" -Message "*memory*"
重要度の低いアプリケーションやサービスのコードまたは構成の問題に対処し、メモリ使用量を減らします。
SQL Server 以外のアプリケーションがリソースを消費している場合は、これらのアプリケーションを停止または再スケジュールするか、別のサーバーで実行することを検討してください。 これらの手順により、外部的なメモリ負荷を軽減できます。
内部メモリ負荷 (SQL Server が原因ではない)
SQL Server 内のモジュール (DLL) によって発生する内部メモリの負荷を診断するには、次の方法を使用します。
SQL Server で Locked Pages in Memory (AWE API) が使用されていない場合、そのメモリのほとんどは、パフォーマンス モニターの Process:Private Bytes カウンター (
SQLServr
インスタンス) に反映されます。 SQL Server エンジン内からの全体的なメモリ使用量は、 SQL Server:Memory Manager: Total Server Memory (KB) カウンターに反映されます。 Process:Private Bytes と SQL Server:Memory Manager: Total Server Memory (KB)の値に大きな違いがある場合、その違いは DLL (リンク サーバー、XP、SQLCLR など) から生じる可能性があります。 たとえば、 Private バイト が 300 GB で、 合計サーバー メモリ が 250 GB の場合、プロセス内のメモリ全体の約 50 GB が SQL Server エンジンの外部から送信されます。SQL Server がメモリ内のロックされたページ (AWE API) を使用している場合、パフォーマンス モニターには個々のプロセスのメモリ使用量を追跡する AWE カウンターが用意されていないため、問題を特定することは困難です。 SQL Server エンジン内の全体的なメモリ使用量は、 SQL Server:Memory Manager: Total Server Memory (KB) カウンターに反映されます。 一般的な Process:Private Bytes 値は、300 MB と 1-2 GB 全体で異なる場合があります。 Process:Private Bytesがこの一般的な用途を超えて大幅に使用されている場合、その違いは DLL (リンク サーバー、XP、SQLCLR など) から生じる可能性があります。 たとえば、 Private バイト カウンターが 4 から 5 GB で、SQL Server がメモリ内のロックされたページ (AWE) を使用している場合、プライベート バイトの大部分が SQL Server エンジンの外部から送信される可能性があります。 これは近似法です。
Tasklist ユーティリティを使用して、SQL Server 領域内に読み込まれる DLL を特定します。
tasklist /M /FI "IMAGENAME eq sqlservr.exe"
また、次のクエリを使用して、読み込まれたモジュール (DLL) を調べて、予期しない何かがあるかどうかを確認することもできます。
SELECT * FROM sys.dm_os_loaded_modules
リンク サーバー モジュールが大量のメモリ消費を引き起こしていると思われる場合は、 Allow inprocess オプションを無効にして、プロセスが不足するように構成できます。 詳細については、「リンク サーバーを作成する」を参照してください。 すべてのリンク サーバー OLE DB プロバイダーがプロセスを使い果たさない可能性があります。 詳細については、製品の製造元にお問い合わせください。
まれに OLE オートメーション オブジェクト (
sp_OA*
) を使用する場合は、コンテキスト値として 4 (ローカル (.exe) OLE サーバーのみ) を指定して、SQL Server の外部のプロセスで実行するようにオブジェクトを構成できます。 詳細については、「sp_OACreate」を参照してください。
SQL Server エンジンによる内部メモリ使用量
SQL Server エンジン内のコンポーネントからの内部メモリ負荷を診断するには、次の方法を使用します。
SQL Server: SQL Server:Buffer Manager および SQL Server: Memory Manager のパフォーマンス モニター カウンターの収集を開始します。
SQL Server メモリ クラーク DMV に対して複数回クエリを実行し、エンジン内でメモリの消費量が最も多い場所を確認します。
SELECT pages_kb, type, name, virtual_memory_committed_kb, awe_allocated_kb FROM sys.dm_os_memory_clerks ORDER BY pages_kb DESC
または、より詳細な
DBCC MEMORYSTATUS
出力と、これらのエラー メッセージが表示されたときにその出力が変化する方法を確認することもできます。DBCC MEMORYSTATUS
明らかに不適切であるメモリ クラークを特定する場合は、そのコンポーネントのメモリ消費量の指定に対処することに重点を置きます。 いくつかの例をこちらに示します。
- メモリ クラーク
MEMORYCLERK_SQLQERESERVATIONS
がメモリを消費している場合は、大量のメモリ許可を使用しているクエリを特定し、インデックスを使用してそれらを最適化するか、書き換えるか (たとえば、ORDER by
を削除)、メモリ許可クエリ ヒントを適用します ( min_grant_percentとmax_grant_percentヒントを参照 )。 リソース ガバナー プールを作成メモリ許可メモリの使用量を制御することもできます。 メモリ許可の詳細については、「 SQL Server でのメモリ許可によって発生するパフォーマンスの低下またはメモリ不足の問題のトラブルシューティングを参照してください。 - 多数のアドホック クエリ プランがキャッシュされている場合、
CACHESTORE_SQLCP
メモリ クラークは大量のメモリを使用します。 クエリ プランを再利用できないパラメーター化されていないクエリを特定し、ストアド プロシージャに変換するか、sp_executesql
を使用するか、FORCED
パラメーター化を使用してパラメーター化します。 trace フラグ 174 を有効にしている場合は、無効にして問題が解決されるかどうかを確認できます。 - オブジェクト プラン のキャッシュ ストア
CACHESTORE_OBJCP
がメモリを消費しすぎる場合は、大量のメモリを使用しているストアド プロシージャ、関数、またはトリガーを特定し、アプリケーションを再設計する可能性があります。 一般に、これは、それぞれが数百のプロシージャを持つ大量のデータベースまたはスキーマが原因で発生する可能性があります。 OBJECTSTORE_LOCK_MANAGER
メモリ クラークが大きなメモリ割り当てを示している場合は、多数のロックを適用するクエリを特定し、インデックスを使用して最適化します。 特定の分離レベルでロックが長時間解放されないトランザクションを短縮するか、ロックエスカレーションが無効になっているかどうかを確認します。- 非常に大きな
TokenAndPermUserStore
(select type, name, pages_kb from sys.dm_os_memory_clerks where name = 'TokenAndPermUserStore'
) が観察された場合は、 trace フラグ 4618 を使用してキャッシュのサイズを制限できます。 MEMORYCLERK_XTP
メモリ クラークからのメモリ内 OLTP に関するメモリの問題が発生した場合は、「Monitor」と「インメモリ OLTP のメモリ使用量のトラブルシューティング」を参照しメモリ不足エラーMemory 最適化 tempdb メタデータ (HkTempDB) を参照してください。
- メモリ クラーク
メモリが使用できるようになる可能性がある迅速な解放
次のアクションを実行すると、一部のメモリが解放され、SQL Server で使用できるようになります。
メモリ構成設定を変更する
次の SQL Server メモリ構成パラメーターを確認し、可能であれば max server memory を増やすことを検討します。
- max server memory
- min server memory
Note
異常な設定に気付いた場合は、必要に応じて修正し、メモリ要件の増加を考慮してください。 既定の設定については、「サーバー メモリの構成オプション」に記載されています。
max サーバー メモリを構成していない場合は特にメモリ内のロックされたページを使用して、OS のメモリを許可するように特定の値に設定することを検討してください。 メモリ内のロックされたページサーバー構成オプションを参照してください。
ワークロードをシステムから変更または移動する
クエリ ワークロードを調査します。同時セッションの数、現在実行中のクエリ、および一時的に停止されたり、別の SQL Server に移動されたりする可能性のある重要度の低いアプリケーションがあるかどうかを確認します。
読み取り専用ワークロードの場合は、Always On 環境の読み取り専用セカンダリ レプリカに移動することを検討してください。 詳細については、「読み取り専用ワークロードを Always On 可用性グループのセカンダリ レプリカに読み込む Always On 可用性グループのセカンダリ レプリカへの読み取り専用アクセスの構成を参照してください。
仮想マシンの適切なメモリ構成を確認する
仮想マシン (VM) で SQL Server を実行している場合は、VM のメモリが過剰にコミットされていないことを確かめます。 VM のメモリを構成する方法については、「 仮想化 - メモリのオーバーコミット」を参照してください。また VM 内でメモリを検出する方法と ESX/ESXi 仮想マシンのパフォーマンスの問題 (メモリのオーバーコミット) をトラブルシューティングする方法を参照してください。
SQL Server 内のメモリを解放する
次の DBCC コマンドを 1 つ以上実行して、複数の SQL Server メモリ キャッシュを解放できます。
DBCC FREESYSTEMCACHE
DBCC FREESESSIONCACHE
DBCC FREEPROCCACHE
SQL Server サービスを再起動する
場合によっては、メモリの重大な枯渇に対処する必要があり、SQL Server がクエリを処理できない場合は、サービスの再起動を検討できます。
特定のシナリオで Resource Governor を使用することを検討する
リソース ガバナーを使用している場合は、リソース プールとワークロード グループの設定を確認して、メモリが過度に制限されていないかどうかを確認することをお勧めします。
物理サーバーまたは仮想サーバーに RAM を追加する
問題が解決しない場合は、さらに調査し、場合によってはサーバー リソース (RAM) を増やす必要があります。