高レイテンシのトラブルシューティング: MongoDB接続問題の診断

MongoDBアプリケーションが個々のクエリは高速にもかかわらず動作が遅い場合、高レイテンシが原因です。この包括的なガイドでは、接続関連のパフォーマンスボトルネックの診断と解決について詳しく説明します。ネットワーク問題のトラブルシューティング、接続プール設定の最適化、全体的な応答性に影響を与えるサーバーリソースの競合(CPU、メモリ、I/O)の特定方法を学びます。実用的なヒントと監視戦略により、レイテンシ問題の正確な原因を特定できます。

高レイテンシのトラブルシューティング: MongoDB接続問題の診断

MongoDBの高レイテンシは、必ずしもクエリが遅いという問題ではありません。クエリ自体はサーバーに到達すれば高速でも、リクエストが接続を待機したり、DNSで停滞したり、低速なネットワーク経路を経由したり、一時的な障害後に再試行したり、大量の結果セットをアプリケーションに返すのに時間がかかりすぎたりすることがあります。

最初の作業は、エンドツーエンドのレイテンシを分割することです。サーバー側のクエリ時間、接続チェックアウト時間、ネットワークラウンドトリップ、結果転送、アプリケーション処理はそれぞれ異なる問題であり、異なる修正が必要です。

1. ネットワーク設定と接続性

ネットワークの問題は、予期しないレイテンシの頻繁な原因です。アプリケーションサーバーとMongoDBインスタンス間のわずかなパケット損失やラウンドトリップ時間(RTT)の増加でも、パフォーマンスに大きな影響を与える可能性があります。

1.1. アプリケーションとMongoDBサーバー間のレイテンシ

  • PingとTraceroute: 標準的なネットワーク診断ツールを使用してRTTを測定し、ネットワーク経路の潜在的なボトルネックを特定します。

    ping <mongodb_host>
    traceroute <mongodb_host>  # Windowsの場合はtracert
    
    • ヒント: 一貫して高いping時間や大きな変動は、ネットワークの不安定性を示している可能性があります。
  • ファイアウォールルールとネットワーク輻輳: ファイアウォールが遅延を引き起こしていないか(例:ディープパケットインスペクション)、ネットワークリンクが飽和していないかを確認します。アプリケーションとデータベース層間のネットワークトラフィックを監視します。

1.2. DNS解決の遅延

ホスト名の代わりにIPアドレスが使用されている場合、DNSルックアップが遅いと、接続試行ごとにレイテンシが追加される可能性があります。DNSサーバーが応答性が高く、正しく設定されていることを確認します。

2. 接続プーリングの問題

接続プーリングはパフォーマンスに不可欠ですが、設定ミスや過剰使用は重大なレイテンシにつながる可能性があります。

2.1. 接続プーリングの理解

接続プーリングは、アプリケーションが再利用できるオープンなデータベース接続のセットを維持し、リクエストごとに新しい接続を確立するオーバーヘッドを回避します。これにより、接続設定時間が大幅に短縮されます。

2.2. 最大接続数の不足

アプリケーションの最大接続プールサイズが低すぎる場合、アプリケーションスレッドが利用可能な接続を待たなければならず、リクエストのキューイングと高レイテンシが発生する可能性があります。逆に、過度に大きなプールはMongoDBサーバーを圧倒する可能性があります。

  • 監視: ほとんどのMongoDBドライバーは、接続プールの使用状況に関する統計情報を提供します。次のようなメトリクスを探します:

    • pool.size: プール内の現在の接続数。
    • pool.in_use: 現在使用中の接続数。
    • pool.waiters: 接続を待機しているスレッド数。

    pool.waitersが一貫して高い場合、maxPoolSizeが小さすぎる可能性があります。

  • 設定(例:Python/PyMongo):

    from pymongo import MongoClient
    
    client = MongoClient(
        'mongodb://localhost:27017/',
        maxPoolSize=20,  # ニーズに基づいてこの値を調整します
        minPoolSize=5
    )
    
    • ヒント: 最適なmaxPoolSizeは、アプリケーションの同時実行性、MongoDBサーバーのコア数、ネットワークレイテンシによって異なります。適度な値から始め、監視に基づいて調整します。

2.3. 接続確立のレイテンシ

プーリングを使用していても、接続の初期確立には時間がかかる場合があります。特に、高レイテンシネットワークやTLS/SSLネゴシエーションが含まれる場合です。このレイテンシは、既存の接続がすべて使用中またはタイムアウトしたためにプールが新しい接続を作成する必要があるときに発生します。

  • TLS/SSLオーバーヘッド: セキュリティには不可欠ですが、TLS/SSLハンドシェイクはオーバーヘッドを追加します。ハードウェアが暗号化/復号化の負荷を処理できることを確認します。

3. MongoDBサーバーリソースの競合

MongoDBサーバー自体がプレッシャー下にある場合、単純な操作でもレイテンシが増加する可能性があります。

3.1. CPU使用率

MongoDBサーバーのCPU使用率が高いと、接続処理やクエリ処理を含むすべての操作が遅くなる可能性があります。これは次の原因で発生する可能性があります:

  • 非効率的なクエリ: コレクション全体のスキャンや複雑な集計を実行するクエリ。

  • 高同時実行性: サーバーの処理能力を圧倒する多すぎる同時リクエスト。

  • バックグラウンド操作: メンテナンスタスク、選挙、データ同期。

  • 監視: mongostatまたはクラウドプロバイダーの監視ツールを使用してCPU使用率を確認します。

    mongostat --host <mongodb_host> --port 27017
    

    高いqr(クエリキューの長さ)とqw(書き込みキューの長さ)を探します。

3.2. メモリ使用量とスワッピング

MongoDBは、ワーキングセット(アクティブに使用されるデータとインデックス)がRAMに収まるときに最適に動作します。RAM不足によりサーバーがディスクへのスワッピングを開始すると、パフォーマンスは大幅に低下します。

  • 監視: MongoDBサーバーのRAM使用量とスワップアクティビティを監視します。

    # Linuxでは、topまたはhtopを使用します
    top
    

    かなりのスワップ使用量(topSwap)が見られる場合、メモリプレッシャーの強い兆候です。

  • 解決策: サーバーRAMを増やすか、MongoDBデプロイメントを最適化してメモリフットプリントを削減します(例:インデックスがクエリをカバーしていることを確認する)。

3.3. ディスクI/Oボトルネック

データやインデックスがメモリに完全にキャッシュされていない場合、特に低速なディスクI/Oが一般的なボトルネックです。

  • 監視: Linuxシステムでiostatを使用してディスク使用率を確認します。

    iostat -xz 5
    

    高い%utilawait、またはsvctm値はディスクの飽和を示します。

  • 解決策: より高速なストレージ(SSD)を使用し、キャッシュに十分なRAMを確保し、ディスク読み取りを減らすためにクエリを最適化します。

3.4. サーバーのネットワークスループット

ネットワーク経路が良好でも、MongoDBサーバーのネットワークインターフェースが大量のリクエストを処理している場合、飽和する可能性があります。

  • 監視: MongoDBサーバー自体のネットワークトラフィックを監視します。

4. アプリケーションレベルの考慮事項

問題がMongoDBやネットワークに直接関係なく、アプリケーションがデータベースとどのように相互作用するかにある場合があります。

4.1. 過剰なドライバー呼び出し

操作をバッチ処理せずに、非常に多くの小さな独立したデータベース呼び出しを行うアプリケーションは、接続オーバーヘッドとレイテンシの増加につながる可能性があります。

  • 例: insert_manyを使用する代わりに、ループ内で個別のinsert_one操作を実行する。

4.2. アプリケーション内の長時間実行操作

アプリケーションがMongoDBからデータを取得した後、応答を返す前に重要な計算やI/Oを実行する場合、これはエンドツーエンドの高レイテンシとして現れます。

  • 解決策: アプリケーションコードをプロファイリングして、これらの低速セクションを特定し最適化します。

ステップバイステップのレイテンシトリアージ

まず、リクエストを分割して測定します。「APIが900ミリ秒かかる」という1つの数値だけでは不十分です。接続の待機、コマンドの送信、MongoDBでの実行、結果の受信、応答のシリアライズにどれだけの時間が費やされているかを知る必要があります。

ほとんどのMongoDBドライバーは、コマンド監視フックを公開しています。コマンドの開始と成功または失敗の周りに一時的なログを追加します。コマンド名、期間、データベース、コレクション、リクエストIDを含めます。機密データが含まれる可能性がある場合は、完全なクエリ値をログに記録しないでください。

コマンドの期間が短いのにAPIが遅い場合、MongoDBが主なボトルネックではない可能性があります。アプリケーションのCPU、ダウンストリームのHTTP呼び出し、JSONシリアライゼーション、テンプレートレンダリング、またはキューの待機を調べます。コマンドの期間が長いのにMongoDBプロファイラーが高速な実行を示している場合、遅延は接続チェックアウト、ネットワーク転送、DNS、TLSネゴシエーション、または結果のデコードにある可能性があります。

接続チェックアウト時間は特に見落とされがちです。プールは起動時は正常でも、トラフィックの急増時に飽和する可能性があります。リクエストがソケットを待機する場合、MongoDBは各コマンドを到着後すぐに実行しても、アプリケーションから見るとすべてのクエリが遅く見えます。ドライバーが公開している場合は、プールの待機時間を追跡します。公開されていない場合は、データベース呼び出しの前後の時間を測定し、サーバー側のプロファイラー時間と比較します。

簡単なローカルテストで問題を絞り込むことができます:

mongosh "mongodb://mongo1.internal:27017/app" --eval 'db.runCommand({ ping: 1 })'

可能であれば、ラップトップ、アプリケーションホスト、同じサブネット内の別のホストから実行します。アプリケーションホストのみが遅い場合、ローカルDNS、ファイアウォールルール、ルーティング、過負荷ノード、またはコンテナネットワーキングが疑われます。すべてのホストが遅い場合、データベース層または層間のネットワーク経路を調べます。

DNSについては、繰り返しルックアップをテストします:

time nslookup mongo1.internal

新しい接続作成時のルックアップが遅いと、クライアントを再利用せずに頻繁に作成するサービスに悪影響を及ぼす可能性があります。ほとんどのアプリケーションでは、プロセスごとに1つのMongoClientを作成し、それを再利用します。リクエストごとに新しいクライアントを作成することは、レイテンシを生み出す最も速い方法の1つです。

TLSも、特に接続作成時にコストを追加する可能性があります。これはTLSを無効にすべきだという意味ではありません。プールされた接続を再利用し、不要なクライアントのチャーンを回避し、ハンドシェイク中にCPUが飽和していないことを確認する必要があるということです。

サーバー側では、MongoDBメトリクスとオペレーティングシステムメトリクスを比較します。mongostatがキューが増加していることを示し、ホストが高いCPUを示している場合、クエリまたは同時実行性のプレッシャーがある可能性があります。CPUが適度でもiostatが高い待機時間を示している場合、ストレージが問題の一部である可能性があります。メモリプレッシャーがスワッピングを引き起こしている場合、まずそれを修正します。スワップするデータベースホストは、すべてをランダムで遅く感じさせます。

大きな結果セットは、接続レイテンシのように見えることがあります。50,000ドキュメントを返すクエリは高速に実行されるかもしれませんが、ネットワーク経由でデータを転送し、ドライバーでデコードするのに時間がかかります。プロジェクション、ページネーション、サーバー側の制限を使用します。APIの場合、開発中に便利だからといってドキュメント全体ではなく、画面が実際に必要とするフィールドを返します。

最後に、トポロジーの動作を確認します。レプリカセットの選挙中は、新しいプライマリが選出されるまで書き込みが一時停止されます。ドライバーもトポロジーの変更を検出する必要があります。レイテンシのスパイクが選挙、ノード再起動、メンテナンスウィンドウ、またはネットワークの瞬断と一致する場合、修正はクエリチューニングではなく、安定性とフェイルオーバー動作にある可能性があります。接続文字列にレプリカセットメンバーまたは適切なSRVレコードが含まれていることを確認し、アプリケーションが長時間ハングするのではなく、予測可能に失敗するようにタイムアウトを意図的に設定します。

有用なインシデントノートは、証拠で終わります:プール待機時間、コマンド期間、プロファイラー期間、ネットワークRTT、CPU、メモリ、ディスクI/O、およびシークレットを削除した正確な接続文字列の形状。これにより、推測の集まりではなく、実際の診断が得られます。

タイムアウト設定は診断の一部

タイムアウトはレイテンシを修正しませんが、レイテンシがユーザーにどのように感じられるかを決定します。サーバー選択タイムアウトが高すぎると、アプリケーションは制御されたエラーを返せるはずの時間を過ぎてもハングする可能性があります。ソケットタイムアウトが低すぎると、データベースが正常でも通常の長時間実行レポートが失敗する可能性があります。ワークロードに合わせて意図的に設定します。

リクエスト-レスポンスAPIの場合、ユーザーが待機しているため、短いサーバー選択タイムアウトが理にかなっていることがよくあります。バッチジョブの場合、長いタイムアウトが許容される場合があります。同じサービスが両方を実行する場合は、それらのクライアントを分離します。ダッシュボードクエリと夜間エクスポートは、常に同じタイムアウトとプール動作を共有する必要はありません。

また、再試行動作も確認します。再試行可能な書き込みとドライバーの再試行は、短いネットワークエラーを平滑化できますが、すべての試行がタイムアウト近くで待機する場合、単一のユーザーリクエストが予想よりも長くかかる可能性があります。可能な場合は再試行回数をログに記録します。すべてのリクエストが静かにバックグラウンドで再試行している場合、再試行後に成功するサービスは依然として正常ではない可能性があります。

接続プールサイジングの平易な説明

より大きなプールが自動的に高速になるわけではありません。データベースが100の同時操作を快適に処理でき、アプリケーションが1,000のビジーな接続を開く場合、コンテキストスイッチ、メモリ使用量、キューイングが増加する可能性があります。プールが小さすぎると、MongoDBに容量があるにもかかわらず、アプリケーションスレッドが待機します。適切なプールサイズは、同時実行性、操作期間、サーバー容量から決まります。

まず、1つのアプリケーションインスタンスから同時にデータベースにヒットするリクエストの数を尋ねます。次に、アプリケーションインスタンスの数を掛けます。1つのプロセスで適度に見えるmaxPoolSizeも、フリート全体では大きくなる可能性があります。プールが100の10のアプリケーションポッドは、管理ツール、ジョブ、その他のサービスをカウントする前に、最大1,000の接続を作成できます。

接続のチャーンに注意してください。接続が常に開閉している場合、その理由を調べてください。アイドルタイムアウト、ロードバランサー、NATゲートウェイ、サーバーレス実行環境、リクエストごとのクライアント作成がすべてチャーンを引き起こす可能性があります。安定したプールされた接続は、通常、より安定したレイテンシを生成します。

短いフィールドチェックリスト

レイテンシがスパイクした場合、すべてを再起動する前に証拠を収集します:

アプリケーション:
- リクエスト期間のパーセンタイル
- データベースコマンド期間
- 接続チェックアウト待機時間
- 再試行回数
- 結果サイズ

MongoDB:
- 低速コマンドのプロファイラーエントリ
- スパイク中の現在の操作
- レプリケーションラグ
- 接続数とキューイングされたリーダー/ライター

ホストとネットワーク:
- CPU飽和度
- メモリプレッシャーとスワップ
- ディスク待機時間/使用率
- パケット損失とRTT
- DNSルックアップ時間

このチェックリストは通常、3つのストーリーのいずれかを指し示します:アプリケーションが接続を待機している、MongoDBがコマンドの実行に時間がかかっている、またはネットワーク/結果転送が高速なコマンドの周りで遅い。各ストーリーには異なる修正があります。

実用的な結びの言葉

MongoDBアプリケーションの高レイテンシのトラブルシューティングには、体系的なアプローチが必要です。ネットワーク接続、接続プール設定、サーバーリソース使用率を調査することで、遅延の根本原因を特定できます。レイテンシは症状であり、アプリケーションとデータベースインフラストラクチャの全体像を把握することが最適なパフォーマンスを達成するための鍵であることを忘れないでください。

最も一般的な原因であるネットワークRTT、接続プールのwaiters、サーバーのCPU/メモリ/ディスクI/Oの監視から始めます。必要に応じて、より具体的な領域に徐々に掘り下げます。これらのメトリクスと設定を定期的に確認することで、レイテンシの問題がユーザーに影響を与えるのを防ぐことができます。