msawady’s tech-note

フルスタックエンジニアの学んだことや考えていること

【MongoDB】ReplicaSet を利用して read アクセスを負荷分散する

MongoDB の負荷分散

  • 分散計算で MongoDB からの読み取りが遅延していたので、ReplicaSet による負荷分散をしました
  • その時の調査方法、具体的なやり方をまとめておきます

起こっていたこと

  • 計算ノードは最大 60 台程度、1ノードあたり 18 計算プロセスが稼働する
    • => 同時に約 1000 プロセスが MongoDB へアクセスする
  • MongoDB に格納されているインプットデータをもとに計算する
    • => アクセスの大半は read アクセス
  • 計算ノードは計算量にしたがって自動的に起動/削除される
  • 計算プロセスは OOM を防ぐために適宜再起動される
    • => コネクションの取得/切断が多く発生する

というユースケースで以下のような事象が発生していました。

  • 計算プロセスが多く立ち上がると read に 5~10 sec(!) ほどかかる
  • DBインスタンスに SSH して mongo コマンドでシェルを取得するのも時間がかかる

調べたこと

  • mongostat でコネクション数を確認: 約 9000 コネクション
  • ps aux -L でスレッド数を確認: 約 9000 スレッド
    • => 1 コネクションで 1 スレッド作成されている
    • => 1 計算プロセスあたり 9~10 コネクションを張っている
      • pymongo はデフォルトでコネクションプールを利用しているため
  • iostat でディスク負荷を確認すると、ほとんどディスクアクセスがない
    • メモリ上にキャッシュした値を返すことが出来ている
  • htop で DB インスタンスのCPU使用状況を確認
    • 18コア(c4.4xlarge) のうち、同時には 4 コアくらいしか利用されていない
  • コンテキストスイッチが怪しいと思い sar -w 1: 秒間 10,000 回以上の cswtch...

遅延の原因は「コネクション数が多い => スレッド数が多い => コンテキストスイッチが増える」 ことにより、CPUが効果的に動けていないことだと推測出来ました。

リードレプリカを作成して read アクセスを分散する

対応策としてリードレプリカを利用してコネクション数を分散します。

MongoDB 側の設定

公式マニュアルを参照して、ReplicaSet を作成します。

docs.mongodb.com

また、2 台のDBインスタンスしか残らないケースを考慮して Arbiter を追加しました。 Secondary -> Primary への昇格のための投票にのみ参加するための Arbiter を追加することで、 2台のうち Primary がダウンした際にもフェイルオーバーが正しく行われるようになります。

docs.mongodb.com

クライアント側の設定

Python の MongoDB クライアント の pymongo のパラメータを変更します。

api.mongodb.com

  • replicaSet を指定して接続することで、 replicaSet への接続が出来る
  • readPreference を指定することで、読み取り先の分散が出来る
    • デフォルトは PRIMARY で Primary からしか read しない
    • 今回ユースケースでは計算中は write が行われないため NEAREST を設定する
  • pymongo のバージョンが 3.X よりも低い(2.X) の場合は ReplicaSetClientを利用する必要がある
    • 3.X では普通に繋ぐときと同様に MongoClient でOK
    • (今回いちばんハマったところ...)

確認

  • mongostat--discovery オプションをつけることでレプリカセットの他のインスタンスの stats も確認できる
    • コネクションが分散されていることを確認する
  • sar -w 1 コマンドで、コンテキストスイッチが減っていることを確認する
  • アプリ側のログから遅延が発生していないことを確認する

その他

  • AWS のマネージドサービスの AWS DocumentDB を利用しなかったのか?
    • 認証がパスワード認証しか対応していなかったのでボツになりました
    • AWS Secrets Manager を利用して平文を残さないようにすることは可能ですが、セットアップの手間が...
  • コアが多いインスタンスを利用する場合は --numactl オプションをつけることでパフォーマンスの改善が見込めるようです
  • LocalThresholdMS を設定することで遠隔地からの読み取りを制御することが出来ます
    • デフォルトで ping 応答時間が 15ms 以上のインスタンスには平常時にはアクセスされません
    • 同一のサブネットにリードレプリカを配置する場合は意識しなくて良い