MongoDBパフォーマンス監視:主要コマンドとメトリクスの解説

主要なシェルコマンドを使用してMongoDBのパフォーマンスをプロアクティブに監視する方法を学びます。このガイドでは、`db.currentOp()`と`db.serverStatus()`を使用した接続状況の追跡、プロファイリングコマンド(`db.setProfilingLevel`)を使用したスロークエリの分析、リソース使用率とインデックスの健全性に関連する重要なメトリクスの解釈方法を詳しく説明し、最適なデータベースチューニングを実現します。

MongoDBパフォーマンス監視:主要コマンドとメトリクスの解説

効果的なデータベース管理は堅牢な監視にかかっています。主要なNoSQLドキュメントデータベースであるMongoDBの場合、パフォーマンスメトリクスを理解することは、高可用性と応答性を維持するために重要です。スロークエリ、過剰なリソース消費、予期しない接続スパイクは、アプリケーションのパフォーマンスに深刻な影響を与える可能性があります。

MongoDBが遅くなったとき、最初に役立つ質問は「データベースが悪いのか」ではありません。「サーバーは今何をしていて、それは通常と異なるのか」です。以下のコマンドは、インデックスを変更したり、ハードウェアを拡張したり、アプリケーションを非難したりする前に、私が最初のパスとして使用するものです。

MongoDBシェル(mongosh)での主要な監視コマンド

これらのコマンドを実行するための主要なインターフェースは、MongoDBシェル(mongosh)、またはレガシーなmongoシェルです。ここに示すすべてのコマンドは、このシェル環境内で実行されます。

1. 現在の接続を理解する:db.currentOp()db.serverStatus()

アクティブな接続を監視することは、接続の枯渇を防ぎ、リソースをブロックしている可能性のある長時間実行操作を特定するために不可欠です。

db.currentOp()

このコマンドは、データベースで現在実行中の操作に関する情報を返します。リアルタイムで低速またはブロックしているクエリを特定するために不可欠です。

使用例:

現在実行中のすべての操作を表示するには:

db.currentOp()

特定のしきい値より長く実行されている操作(例:5秒以上実行されている操作)を探すには:

db.currentOp({"secs_running": {$gt: 5}})

出力には、opns(名前空間)、querysecs_runningなどの詳細が含まれます。

db.serverStatus()

このコマンドは包括的なステータス情報を提供しますが、そのconnectionsセクションは接続プーリングと制限を監視するために重要です。

serverStatus内の主要なメトリクス(接続セクション):

  • current:サーバーへのアクティブな接続数。
  • available:確立可能な利用可能な接続数(設定された最大値に基づく)。
db.serverStatus().connections

2. クエリパフォーマンスの分析:db.getProfilingStatus()db.setProfilingLevel()

MongoDBは、データベース操作の実行詳細をログに記録する組み込みのプロファイリングツールを提供し、リソースを大量に消費するクエリを特定できるようにします。

プロファイリングレベル

プロファイリングレベルは、どの操作がログに記録されるかを決定します:

  • 0(オフ): 操作はプロファイリングされません。
  • 1(スロー操作): 設定されたしきい値(slowms)より遅い操作のみがプロファイリングされます。
  • 2(すべての操作): すべての操作がプロファイリングされます。これにより、かなりの書き込み負荷が発生するため、対象を絞ったトラブルシューティングのためにのみ短時間使用する必要があります。

ステータスの確認

現在のプロファイリングレベルを確認するには:

db.getProfilingStatus()

レベルの設定(例)

スロー操作のみのプロファイリングを有効にするには(100ミリ秒を超える操作):

// slowmsを100ミリ秒に設定(デフォルトは通常100)
db.setProfilingLevel(1, { slowms: 100 })

ヒント: 必要な情報を収集した後は、過剰なログ記録によるパフォーマンス低下を防ぐために、必ずプロファイリングをレベル0に戻してください。

プロファイリングされたスロークエリの表示

プロファイリングされた操作は、監視対象の特定のデータベース内のsystem.profileコレクションに保存されます。過去1時間で最も遅い10件のクエリを表示するには:

db.system.profile.find().sort({millis: -1}).limit(10).pretty()

3. リソース使用率メトリクス

MongoDBがCPU、メモリ、I/Oリソースをどのように使用しているかを理解することは、スケーリングの決定に不可欠です。

メモリとストレージの使用状況:db.serverStatus()

serverStatus内のglobalLockセクションとstorageEngineセクションは、リソース管理に関する深い洞察を提供します。

メモリ指標:

  • resident:プロセスが使用している物理メモリの量。
  • virtual:プロセスによって割り当てられた仮想メモリの合計。
db.serverStatus().globalLock

ロック競合の監視

MongoDBは内部ロックメカニズムを使用しています。ロックの取得と待機を監視することで、同時実行性のボトルネックを特定できます。

globalLockの主要なメトリクス:

  • currentQueue.readers:ロックを待機しているリーダーの数。
  • currentQueue.writers:ロックを待機しているライターの数。
  • totalTime:すべての操作でロックを待機した合計時間。

currentQueueの値が高い場合、多くの場合、インデックスが不足しているか、書き込み操作が過度に長く、リーダー/ライターがキューに入れられていることを示しています。

4. インデックスの使用状況と健全性:db.collection.stats()

十分に活用されていない、または欠落しているインデックスは、パフォーマンス低下の最も一般的な原因です。stats()コマンドは、インデックスの効率を分析するのに役立ちます。

特定のコレクション(例:users)で実行した場合:

db.users.stats()

確認すべき主要なメトリクス:

  • totalIndexSize:そのコレクションのすべてのインデックスが消費するディスク容量の合計。
  • indexSizes:インデックスごとの容量使用量の内訳。
  • インデックスが存在しても読み取りに使用されない場合、それはオーバーヘッドであり、削除を検討する必要があります。

5. ディスクI/Oとスループット:db.serverStatus()(ネットワークと操作)

ネットワークアクティビティと操作のレートを監視すると、データベースのスループットを把握できます。

操作レート(opcountersから):

opcountersは、サーバーが最後に再起動されてから実行された操作の総数を追跡し、タイプごとに分類されます:

  • insertqueryupdatedeletegetmorecommand

時間の経過に伴うこれらのカウンターの変化を追跡することで(例:2つの連続したserverStatus呼び出しを比較)、操作スループット(1秒あたりの操作数)を計算できます。

比較例:

  1. 時間T1でdb.serverStatus().opcountersを実行します。
  2. 時間T2でdb.serverStatus().opcountersを実行します。
  3. T1の値をT2の値から減算して、その間隔で実行された操作の総数を取得します。

プロアクティブな監視のためのベストプラクティス

  • 自動化が鍵: 手動のシェルコマンドのみに依存するのは非効率的です。これらのエンドポイントを自動的にクエリするMongoDB Cloud Manager/Ops Managerやサードパーティの監視ソリューションなどのツールを使用して監視を統合します。
  • ベースラインを確立する: システムが正常なときにコマンドを実行して、パフォーマンスのベースラインを確立します。このベースラインからの逸脱は、直ちに調査する必要があります。
  • レイテンシに焦点を当てる: 操作数は有用ですが、エンドユーザーエクスペリエンスの問題を診断する際には、生のスループットよりもレイテンシメトリクス(プロファイリングログによって報告される時間など)を優先します。
  • 接続を頻繁に確認する: トラフィックの多いアプリケーションでは、接続制限に最初に達することがよくあります。設定された最大値に対するdb.serverStatus().connections.currentを監視します。

実用的な最初のパスチェックリスト

誰かが「MongoDBが遅い」と言ったとき、すぐにインデックスの変更に飛びつかないでください。短いチェックリストから始めて、見たものを書き留めてください。

サーバーがアクティブな操作で過負荷になっていないか確認します:

db.currentOp({
  active: true,
  secs_running: { $gt: 2 }
});

いくつかの長時間実行操作は、分析ジョブでは正常な場合があります。大量の書き込み、コレクションスキャン、またはブロックされた操作が多数ある場合は別です。nsの名前空間、opの操作タイプ、クエリの形状を確認します。多くの操作が1つの更新やインデックスビルドの背後で待機している場合、修正方法は読み取りクエリのインデックス不足とは異なります。

次に接続を確認します:

db.serverStatus().connections;

currentが急速に上昇している場合、アプリケーションの接続プールが誤って設定された、デプロイによってワーカーが多すぎる、またはクライアントがタイムアウトして再接続している可能性があります。availableがゼロに近い場合、新しいクライアントが接続に失敗する可能性があるため、緊急のシグナルです。正しい答えは、サーバー制限を引き上げることではなく、アプリケーションでのプールチューニングである可能性があります。

次に、操作カウンターを短い間隔で2回確認します:

const a = db.serverStatus().opcounters;
sleep(5000);
const b = db.serverStatus().opcounters;
printjson({
  insertPer5s: b.insert - a.insert,
  queryPer5s: b.query - a.query,
  updatePer5s: b.update - a.update,
  deletePer5s: b.delete - a.delete,
  commandPer5s: b.command - a.command
});

起動時からのカウンターは長期的なコンテキストに役立ちますが、既知の間隔での差異は、現在何が起こっているかを示します。コマンドトラフィックは多いがクエリが少ない場合、通常の読み取りではなく、メタデータチェック、監視ノイズ、またはドライバーの動作を調べている可能性があります。

ハードウェアを非難する前にexplain()を使用する

プロファイルコレクションは、どの操作が遅いかを示します。explain()は、CPUやメモリを追加する前に、クエリが遅い理由を理解するのに役立ちます。

db.users.find({ email: "[email protected]" }).explain("executionStats");

出力で、totalDocsExaminednReturnedを比較します。MongoDBが1人のユーザーを返すために膨大な数のドキュメントを調べている場合、クエリにはより適切なインデックスまたは別のフィルターが必要になる可能性があります。totalKeysExaminedが高い場合、インデックスは存在しますが、クエリパターンに対して十分に選択的ではない可能性があります。

複合クエリの場合、インデックスの順序が重要です:

db.orders.find({
  accountId: "acct_123",
  status: "open",
  createdAt: { $gte: ISODate("2025-11-01T00:00:00Z") }
}).sort({ createdAt: -1 });

有用なインデックスは次のようになります:

db.orders.createIndex({ accountId: 1, status: 1, createdAt: -1 });

これは普遍的なルールではありません。最適なインデックスは、カーディナリティ、ソート順、およびコレクションにヒットするクエリの完全なセットによって異なります。重要なのは、推測する代わりにデータベースに実行計画を表示させることです。

過剰反応せずにプロファイリングデータを読む

プロファイリングレベル2はすべての操作をログに記録し、ビジーなシステムではオーバーヘッドが追加される可能性があります。短い対象期間のみ使用してください。適切なslowmsしきい値を持つレベル1は、スロー操作を見つけるためにより安全です。

db.setProfilingLevel(1, { slowms: 200 });

データを収集した後、最も遅いエントリを検査します:

db.system.profile.find(
  {},
  {
    ns: 1,
    op: 1,
    millis: 1,
    command: 1,
    keysExamined: 1,
    docsExamined: 1,
    nreturned: 1
  }
).sort({ millis: -1 }).limit(20).pretty();

1つのスロークエリが必ずしも本番インシデントを意味するわけではありません。スケジュールされたレポート、再起動後のコールドキャッシュ、またはまれなメンテナンスタスクが上位に表示される可能性があります。単一のサンプルよりもパターンの方が重要です。同じクエリ形状が繰り返し表示され、返されるドキュメントよりもはるかに多くのドキュメントを調べている場合、それは実際のチューニング候補です。

レプリカセットとストレージプレッシャーの監視

レプリカセットの場合、パフォーマンスはプライマリだけの問題ではありません。遅れているセカンダリは、フェイルオーバーの信頼性と、クライアントがセカンダリ読み取りを使用する場合の読み取りワークロードに影響を与える可能性があります。

rs.status();

正常でないメンバー、予期しない状態変化、または回復しないレプリケーションラグがないか確認します。許容可能な正確なラグはアプリケーションによって異なります。キュー状のワークロードは多少の遅延を許容する場合があります。ほぼリアルタイムの読み取りを約束するダッシュボードは、そうでない場合があります。

ストレージプレッシャーも同じコンテキストが必要です。db.serverStatus()はストレージエンジンとWiredTigerメトリクスを表示できますが、ディスクレベルのツールも依然として重要です。MongoDBが低速ディスクを待機している場合、データベース内のシェルコマンドは根本原因ではなく症状を示します。ディスクレイテンシ、ファイルシステム使用率、CPUスティール、メモリプレッシャーなどのホストメトリクスと関連付けます。

手動チェックをアラートに変える

手動コマンドは調査中に最適です。通常の運用では、有用なシグナルを自動チェックに変換します:接続使用率、レプリケーションの健全性、スロークエリレート、ディスク使用率、利用可能な場合はページフォールトまたはキャッシュプレッシャー、操作レイテンシ。1分ごとのスパイクではなく、持続的な悪い動作にアラートを出します。

適切なアラートにはコンテキストが含まれます。「MongoDBスロークエリが多い」は、データベース、コレクション、クエリ形状、現在のレート、および最近のプロファイルサンプルまたはダッシュボードパネルへのリンクを含むアラートよりも役立ちません。目標は、インシデントの最初の10分間を短縮することです。

スローダウン中にしてはいけないこと

一度に複数の変更を加えないでください。同じインシデントでインデックスの追加、接続制限の増加、アプリケーションの再起動、プールサイズの変更を行うと、症状が解消される可能性がありますが、どのアクションが役立ったかはわかりません。1つの変更を行い、改善されるべきメトリクスを監視し、メモを取ります。

killOpには注意してください。1つの操作が明らかに有害な場合に役立ちますが、ランダムな長時間実行操作を強制終了すると、アプリケーションの動作が悪化する可能性があります。操作が移行、バックアップ、インデックスビルド、またはレポートジョブに属している場合、データベースが深刻な問題に陥っていない限り、停止する前に所有者を特定してください。

serverStatus()を単一の魔法のヘルススコアとして扱わないでください。これはカウンターとスナップショットのコレクションです。値が高いことは大規模でビジーなシステムでは正常である可能性があり、値が低いことは小規模でレイテンシに敏感なシステムでは悪い可能性があります。有用な質問は、値がユーザーが直面する問題と一致する方法で変化したかどうかです。

また、データベースの症状とデプロイメントの症状を区別します。クエリ形状を変更したり、より大きな接続プールを開いたり、バックグラウンド移行を開始したりする新しいリリースは、MongoDBを根本原因のように見せかける可能性があります。データベースのみの修正を行う前に、スロー操作のタイミングをデプロイ、ジョブスケジュール、バックアップ、トラフィックの変更と比較します。

MongoDBの監視は、現在の動作を既知のベースラインと比較するときに最も効果的に機能します。db.currentOp()db.serverStatus()、プロファイリング、explain()、およびレプリカセットチェックは、問題がクエリ、インデックス、クライアント接続動作、レプリケーション、またはデータベースの基盤となるホストのいずれであるかを判断するのに十分な証拠を提供します。