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 を作成します。 また、2 台のDBインスタンスしか残らないケースを考慮して Arbiter を追加しました。 Secondary -> Primary への昇格のための投票にのみ参加するための Arbiter を追加することで、 2台のうち Primary がダウンした際にもフェイルオーバーが正しく行われるようになります。
クライアント側の設定
Python の MongoDB クライアント の pymongo
のパラメータを変更します。
replicaSet
を指定して接続することで、 replicaSet への接続が出来るreadPreference
を指定することで、読み取り先の分散が出来る- デフォルトは
PRIMARY
で Primary からしか read しない - 今回ユースケースでは計算中は write が行われないため
NEAREST
を設定する
- デフォルトは
pymongo
のバージョンが 3.X よりも低い(2.X) の場合はReplicaSetClient
を利用する必要がある- 3.X では普通に繋ぐときと同様に
MongoClient
でOK - (今回いちばんハマったところ...)
- 3.X では普通に繋ぐときと同様に
確認
mongostat
に--discovery
オプションをつけることでレプリカセットの他のインスタンスの stats も確認できる- コネクションが分散されていることを確認する
sar -w 1
コマンドで、コンテキストスイッチが減っていることを確認する- アプリ側のログから遅延が発生していないことを確認する
その他
- AWS のマネージドサービスの
AWS DocumentDB
を利用しなかったのか?- 認証がパスワード認証しか対応していなかったのでボツになりました
AWS Secrets Manager
を利用して平文を残さないようにすることは可能ですが、セットアップの手間が...
- コアが多いインスタンスを利用する場合は
--numactl
オプションをつけることでパフォーマンスの改善が見込めるようです- https://docs.mongodb.com/manual/administration/production-notes/#mongodb-and-numa-hardware
- 今回はインスタンスのサイズを落として台数を増やしたので一旦は不要になりました
LocalThresholdMS
を設定することで遠隔地からの読み取りを制御することが出来ます- デフォルトで
ping
応答時間が 15ms 以上のインスタンスには平常時にはアクセスされません - 同一のサブネットにリードレプリカを配置する場合は意識しなくて良い
- デフォルトで