node.js + mongodbで気をつけたほうがいいこと

今まで主にクライアントサイドのプログラミングをしていたのですが新規サービスでサーバサイドを任されたため、その時に多少ノウハウがあったnodeとnode使うならmongodbだろと安直な考えでサービスを作成して2ヶ月半が経った。備忘録も兼ねて気をつけたほうが良かったと思うことをまとめておこうかと思う。

ちなみに以下のサービス。android版もリリースされたので良かったら使ってみてね♡

タップル誕生 – https://tapple.me

mongodbでindex設計は超重要

サービス初期段階ではデータ数が少なくて問題なかったがデータが多くなるにつれてindexがついていないクエリがどんどん重くなった。100万レコードを超えたあたりからindexを使わずに引いてしまうとかなり遅くなってしまう。しかし安直にindexを貼ってしまうとupdate/insertで時間がかかってしまう。なので、indexは極力貼らずに_idのみで引っ張ってこれるようにデータをいろいろな場所に置いておくのが一番良いと今のところ思っている。具体的にどういうことかというと、userが記事を投稿するとuserは記事idを、記事はuseridを持つように設計する。こうすることで、userが投稿した記事一覧を取得するときにはユーザが持っている記事idを$inでmongodbに渡してあげればkey検索となり激速で取得することが出来る。ソート等も$inでid一覧を渡してあげればそこまで性能劣化は起こらなかった。当たり前っちゃー当たり前の話。

大量のデータを書き込む場合は小分けにする

mongodbのクソ仕様としてwrite lock中はすべての処理が止まるというのがある。あるcollectionにデータを書込していれば他のcollectionのreadだろうがなんだろうがwriteが終わるまで待たされる。なんでそんな設計なのか小一時間説教したいが仕様なので置いとくとして、この動作のせいで大量のデータを数msで書き込もうとするとwrite lockが雪だるま式に膨らみmongodbが固まることになってしまう。今のサービスでは数千件のレコードを更新する処理を一気に流していたため、その処理が走ると数分サービスの全てのAPIが待たされるという事になってしまった。対策としては単純で分割してゆっくり書き込むようにする。write/readで一過性がないと問題がある処理は除くとしてそれ以外の処理は書き込みは遅いものと割り切ってしまいゆっくりと書き込んでいくほうが全体的なパフォーマンスは上がる。

nodeで重い処理はしない

nodeはsigle threadのイベント駆動で動いている。なので、1つのeventで重い処理をしてしまうと他の処理がすべて止まる。数千件のデータをforでsortとかしてしまうとその処理がnodeを専有してしまうことになり、その後に控えている軽い処理も全て遅くなる。なのでnodeで重い処理はやってはいけない。やるならasyncモジュール等を使用してeventを阻害しない程度の小さなループを大量に行うようにすればいい。そうすると処理と処理の切れ目で他の軽い処理が走るので全体的に見るとパフォーマンスは上がることになる。

まとめ

こうやって書いていくとあたりまえのことが多い。けど、最初の舵がずれているとどんどんずれていって軌道修正するのが時間が経てば経つほど難しくなるので最初から気をつけておくといいのかと思う。