一般的なMongoDBレプリケーションラグ問題の診断と解決

この包括的なガイドでMongoDBレプリケーションラグの複雑さをナビゲートします。レプリカセットのデータ一貫性と高可用性を損なう一般的な問題を特定、診断、解決する方法を学びます。この記事では、oplogの理解や`rs.status()`によるラグの検出から、不十分なoplogサイズ、ネットワークボトルネック、リソース制約、欠落したインデックスに対する実用的な解決策までをカバーしています。健全で高性能かつ回復力のあるMongoDB環境を維持するための実践的な戦略とベストプラクティスを身につけましょう。

一般的なMongoDBレプリケーションラグ問題の診断と解決

MongoDBのレプリケーションラグは、ダッシュボード上の単なる数字ではありません。それはアプリケーションの動作を変えます。ユーザーがプロフィールを更新し、別のリクエストがセカンダリから読み取ると、古い値が返ってきます。ノードが障害を起こしても、最適なセカンダリがまだ遅れているため、フェイルオーバーに予想以上の時間がかかります。レポートクエリが間違ったメンバーに到達すると、レプリカセットは健全に見えますが、1つのセカンダリだけがプライマリからどんどん離れていきます。

レプリケーションラグを考える便利な方法は単純です。プライマリがoplogエントリを生成する速度が、1つ以上のセカンダリがそれらをフェッチして適用する速度よりも速いということです。修正方法は、あなたの環境でその文のどちら側が真実かによって異なります。プライマリがバーストで書き込みすぎている場合もあります。セカンダリの性能が不足している場合もあります。ネットワークが遅い場合もあります。secondaryDelaySecsで構成されているため、ラグが意図的である場合もあります。変更を加える前に、まずこれらのケースを区別することが最初の仕事です。

まずはラグの実際の形状を確認する

oplogのサイズを変更したり、mongodを再起動したりしてはいけません。まず、ラグが安定しているか、断続的か、1つのメンバーに限定されているか、すべてのセカンダリに影響しているかを調べてください。

mongoshで、以下から始めます:

rs.status()

各メンバーのstateStroptimeDatelastHeartbeatMessage、およびhealthフィールドを確認します。1つのセカンダリが遅れていて、他のセカンダリが最新である場合、おそらくメンバー固有の問題(ディスク、CPU、ローカル読み取り、ローカルメンテナンス、またはネットワークパスの不良)があります。すべてのセカンダリが遅れている場合は、プライマリの書き込みボリューム、プライマリからのネットワークスループット、または異常に大きな操作をより詳しく調べてください。

oplogウィンドウを簡単に確認するには、以下を実行します:

rs.printReplicationInfo()

oplogウィンドウは、現在のoplogでカバーされる時間を示します。これはレプリケーションが健全であることを示すものではありません。セカンダリが初期同期を必要とするリスクを冒す前に、どれだけ遡ることができるかを示します。oplogウィンドウが6時間で、メンテナンスウィンドウが通常8時間かかる場合、現在のラグがゼロであっても、実際の運用リスクがあります。

セカンダリの場合、これも便利です:

rs.printSecondaryReplicationInfo()

古い例ではrs.printSlaveReplicationInfo()を見ることがあります。新しい言い回しでは「secondary」を使用しますが、古いシェルヘルパーや古いブログ記事では「slave」が使われている場合があります。名前よりもフィールドの方が重要です。

ライブシェル用の小さなスクリプトが必要な場合は、プライマリのoptimeと各セカンダリを比較します:

const status = rs.status();
const primary = status.members.find(m => m.stateStr === "PRIMARY");

status.members
  .filter(m => m.stateStr === "SECONDARY")
  .forEach(m => {
    const lagSeconds = (primary.optimeDate - m.optimeDate) / 1000;
    print(`${m.name}: ${lagSeconds}s behind primary`);
  });

これはスナップショットとして扱い、診断として扱わないでください。バッチインポート中に20秒遅れているセカンダリは、すぐに追いつくのであれば問題ないかもしれません。通常のトラフィック中に常に20秒遅れているセカンダリは注意が必要です。

ラグが意図的かどうかを確認する

誤ったインシデントを追跡する前に、レプリカセットの構成を検査します:

rs.conf()

遅延メンバーは、設計上プライマリより遅れるように構成されています。最新のMongoDB構成では、メンバーのsecondaryDelaySecsを探します。そのメンバーは、短期間データの古いビューを保持できるため、一部のリカバリシナリオで役立ちます。新鮮な読み取りには使用すべきではなく、その予想される遅延は通常のラグアラートから除外する必要があります。

実際の運用で見られる間違いは、すべての遅延メンバーをあたかも壊れているかのようにアラートを出すことです。構成された遅延を超えた遅延に対してアラートを出します。メンバーが1時間遅延するように設定されていて、1時間5分のラグを示している場合、実際のラグは約5分です。

oplogウィンドウが小さすぎる場合

oplogはlocalデータベース内のcappedコレクションです。セカンダリはそれを読み取り、順番に操作を適用します。セカンダリが十分に遅れ、プライマリが必要なoplogエントリを保持しなくなった場合、通常のキャッチアップは不可能になります。メンバーは通常、初期同期または適切なバックアップからのリストアが必要です。

これがoplogウィンドウが重要である理由です。予想されるダウンタイム、メンテナンス、ネットワーク中断、およびピーク書き込みバーストをカバーする必要があります。普遍的な「正しい」oplogサイズはありません。静かなクラスターでは、小さなoplogで何日もの履歴を保持できます。更新が多いビジーなクラスターでは、同じサイズを短期間で使い切る可能性があります。

oplogウィンドウがピークトラフィック時に縮小している場合は、次のメンテナンスウィンドウの前に増やします。サポートされているMongoDBバージョンでは、local.oplog.rsを削除して再作成するのではなく、replSetResizeOplogを使用します。レプリカセットメンバーでoplogを削除することは、リスクの高いリカバリ操作であり、通常のチューニング手順ではありません。

oplogをリサイズするメンバーでリサイズコマンドを実行します:

use admin
db.adminCommand({ replSetResizeOplog: 1, size: 10240 })

sizeの値はメガバイト単位です。10240は約10 GBを意味します。必要に応じて各メンバーをリサイズします。MongoDB Atlasなどの管理環境では、直接的なファイルシステムやプロセスの制御を想定するのではなく、プラットフォームのサポートされている構成パスを使用します。

リサイズ後、実際の書き込み負荷の下で新しいウィンドウを確認します。oplogを大きくすると、oplogから脱落する可能性は減りますが、遅いセカンダリが操作をより速く適用できるようにはなりません。

1つのセカンダリが遅い場合

1つのセカンダリだけが遅れている場合、そのホストにログインし、通常のシステム症状を確認します。MongoDBは、実際にはディスクの飽和である問題の原因として非難されることがよくあります。

次のようなツールを使用します:

iostat -xz 1
vmstat 1
top
mongostat --host secondary.example.com:27017
mongotop --host secondary.example.com:27017

高いディスク使用率、高い待機時間、または長いI/Oキューは、通常、セカンダリが十分な速さで書き込めないことを意味します。これは、セカンダリに安価なインスタンスタイプが使用されている場合、EBSやネットワークストレージのプロビジョニングされたスループットが低い場合、またはバックアップやファイルシステムスナップショットがピークアプリケーション書き込みと同時に実行される場合に発生する可能性があります。

CPUも重要です。特に、圧縮、暗号化、ドキュメントの移動、インデックスのメンテナンス、または多数の小さな更新があるワークロードでは顕著です。メモリプレッシャーは、ページフォールト、キャッシュのチャーン、およびoplogエントリを適用しようとしながらディスクから読み取り続けるセカンダリとして現れます。

実用的な修正は通常退屈です。セカンダリにプライマリと同等のストレージとCPUを提供し、そのホストでの競合作業を減らすか、重い読み取りを別の場所に移動します。レプリカセットメンバーは無料のレポート容量ではありません。それでもレプリケーションに追いつく必要があります。

セカンダリでの読み取りが問題を引き起こす場合

セカンダリでの読み取りスケーリングは便利ですが、やり過ぎは簡単です。大規模なコレクションをスキャンするダッシュボードクエリは、oplogの適用と競合する可能性があります。セカンダリは依然として読み取りを受け入れるかもしれませんが、同じCPU、キャッシュ、ディスクがユーザークエリに使用されているため、レプリケーションが遅れます。

遅延しているメンバーのプロファイラと現在の操作を確認します:

db.currentOp({ active: true })

長時間の読み取り、集計ジョブ、またはメンテナンススクリプトが見られる場合、そのセカンダリが本当にそのワークロードを処理すべきかどうかを判断します。レポート用途には、非表示または専用のセカンダリが適している場合があります。アプリケーションの読み取りには、maxStalenessSecondsを設定して、ドライバーが遅れすぎているセカンダリを回避するようにします。

一貫性が重要なパスには、プライマリ読み取りを使用します。例としては、ログイン状態、チェックアウト確認、パスワード変更、アカウント設定、およびユーザーが自分の書き込みをすぐに読み取ることを期待するものがあります。セカンダリ読み取りは、短い古さが許容されるデータに最適です。

プライマリがバーストを生成する場合

大規模な書き込みは、健全なセカンダリを壊れているように見せることがあります。一括インポート、広範囲のマルチドキュメント更新、TTLクリーンアップ、大規模な削除、およびインデックス変更は、適用に時間がかかるoplogアクティビティのバーストを生成する可能性があります。

プライマリでの最近の操作を確認します:

db.currentOp({ active: true })

また、アプリケーションのデプロイ、データ修復ジョブ、バックフィル、およびスケジュールされたタスクを確認します。ちょうど02:00に始まるレプリケーションラグは、しばしば謎ではありません。それはバッチジョブです。

ジョブを制御できる場合は、より小さなチャンクに分割します。たとえば、_idの範囲でドキュメントを更新し、バッチ間に一時停止し、ジョブの実行中にラグを監視します。bulkWriteでは、順序なし書き込みがスループットを向上させる可能性がありますが、エラー処理は明示的である必要があります。失敗は部分的である可能性があるためです。目標は常にプライマリを可能な限り速く終了させることではありません。目標は、レプリカセットがリカバリマージンを失うことなく作業を吸収できるようにすることです。

インデックスとoplogの適用

通常のレプリカセットでは、インデックスは複製されます。手動作業、メンテナンスの失敗、または誤って復元されたノードが原因でメンバー間でインデックスが異なる場合、セカンダリは更新と削除の適用で非常に遅くなる可能性があります。oplog操作はドキュメントを見つける必要があるかもしれませんが、期待されるインデックスがないと、セカンダリはプライマリよりもはるかに多くの作業を行う可能性があります。

影響を受けるコレクションのインデックス定義を比較します:

db.orders.getIndexes()

プライマリと遅延しているセカンダリで同じコマンドを実行します。それらが異なる場合、さらに変更を加える前に理由を調べてください。大規模なインデックスを再構築すると、それ自体が負荷を生み出す可能性があるため、静かな期間に計画するか、差異が広範囲に及ぶ場合は既知の良好なソースからメンバーを再構築します。

バックグラウンドインデックスビルドがすべてのレプリケーションの問題を解決するという古いアドバイスを使用しないでください。MongoDBのインデックスビルド動作はバージョンによって変更されており、適切な運用上の選択は、使用しているバージョンとトポロジに依存します。実行している正確なバージョンの現在のサーバードキュメントを使用してください。

ネットワークの問題は通常、他の場所で見える

ネットワークラグは、不安定なハートビート、断続的なエラー、または特定のホストやリージョン間のスループット低下として現れる傾向があります。基本的なチェックは依然として役立ちます:

ping primary.example.com
traceroute primary.example.com

しかし、低いpingレイテンシは十分な帯域幅を証明しません。レプリケーションは、スループット、パケットロス、ファイアウォール検査、クロスリージョンリンク、またはノイズの多い共有ネットワーキングによって制限される可能性があります。ラグがリモートセカンダリにのみ現れる場合、プライマリと同じリージョンのセカンダリと比較します。同じリージョンのメンバーが正常でリモートメンバーが遅れている場合、トポロジがリンクに過剰な要求をしている可能性があります。

クロスリージョンレプリカセットの場合、トレードオフについて正直になりましょう。これらはディザスタリカバリに役立ちますが、レイテンシと帯域幅の制限に対してより脆弱です。リモートメンバーが読み取り用である場合、古さの制御を使用し、ローカルセカンダリのように動作することを想定するのではなく、フェイルオーバーの動作をテストします。

再起動と再同期のアドバイスに注意する

mongodを再起動すると、一時的な問題が解消される可能性がありますが、ノードがoplogから脱落しそうな場合、インシデントを悪化させる可能性もあります。再起動する前に、oplogウィンドウと現在のラグを確認します。ノードがキャッチアップに2時間必要で、ピークトラフィック時にoplogウィンドウがわずか3時間の場合、長時間の再起動はキャッチアップではなく初期同期で終わる可能性があります。

初期同期は、セカンダリが古くなっている、破損している、または必要なoplog履歴がない場合の有効な修復オプションです。また、コストがかかります。データをコピーし、インデックスを構築し、同期ソースからネットワークとディスクリソースを消費します。本番環境では、レプリカセットが障害に耐えるのに十分な投票権とデータ保持メンバーを維持できるように、一度に1つのメンバーを追加または再構築することをお勧めします。

メンバーが非常に遅れていてキャッチアップできない場合、運用基準に合った新しいバックアップまたはスナップショットベースのパスを取ります。チェックリストにそう書いてあるからといって、データディレクトリを削除しないでください。メンバーが使い捨て可能であること、レプリカセットが再構築に耐えられること、および十分なoplogウィンドウまたは信頼性の高い初期同期ソースがあることを確認します。

ユーザーと運用担当者が気にする点にアラートを設定する

良いアラートは、すべてのシステムで「レプリケーションラグが1秒を超えている」というものではありません。一部のアプリケーションは、分析読み取りで30秒の遅延に耐えられます。他のアプリケーションは、アカウント状態の古い読み取りに耐えられません。アラートのしきい値は、ユースケースを反映する必要があります。

有用なアラートには以下が含まれます:

  • アプリケーションの許容範囲を超えたレプリケーションラグが持続している。
  • oplogウィンドウが、予想される最長のメンテナンスまたはリカバリ間隔を下回っている。
  • セカンダリが予想よりも長くRECOVERINGSTARTUP2、または異常な状態にある。
  • データ保持メンバーのディスクI/O飽和。
  • メンバー間のハートビート障害またはネットワークエラー。

ダッシュボードには、ラグと書き込みボリューム、ディスクレイテンシ、CPU、メモリプレッシャー、ネットワークスループットを並べて表示する必要があります。ラグだけでは問題があることがわかります。隣接するグラフは通常、どの問題かを示します。

実用的なトリアージ順序

オンコールのときは、次の順序を使用します:

  1. rs.status()でどのメンバーが遅れているかを確認します。
  2. secondaryDelaySecsによる意図的なラグがないか確認します。
  3. rs.printReplicationInfo()でoplogウィンドウを確認します。
  4. ラグを書き込みスパイク、バッチジョブ、最近のデプロイと比較します。
  5. 遅延しているセカンダリのディスク、CPU、メモリ、ローカルクエリ負荷を検査します。
  6. 影響を受けるメンバー間のネットワークエラーとレイテンシを確認します。
  7. メンバーがキャッチアップできるか、負荷を除去する必要があるか、より多くのリソースが必要か、または再構築する必要があるかを判断します。

最良の結果は、通常、劇的なコマンドではありません。ボトルネックを見つけ、データの分岐を生じさせずにそれを除去することです。MongoDBのレプリケーションラグは、それを容量とトポロジのシグナルとして扱い、一般的なMongoDBの障害として扱わない場合、管理可能です。