ニコニココメントサーバーにおけるメモリ使用量増大問題の調査と対策 - ドワンゴ 研究開発ブログ(情報元のブックマーク数)

ニコニコ動画のコメントサーバのメモリ使用量増大に関するトラブルシューティングというか改善の話。これは素晴らしい日記だ。

コメントサーバーは、ニコニコ関連サービスのコメントを司るサーバーである。本稿は、ニコニコ広場で起こったコメントサーバーメモリ使用量増大問題について、我々コメントサーバー担当が行った調査と対策のまとめである。
今回のメモリ増大問題の解決にあたり、「仮説を立てる + 計測する→修正する→確認する」というパターンを繰り返した。このパターンは、ソフトウェアの様々な問題を調査するのに適用できる、基本パターンである。

http://info.dwango.co.jp/rd/2010/10/post-3.html

メモリリークの調査とメモリを消費している部分の調査、、、リークだけじゃないのか・・・

行った対策

今回のメモリ増大調査より以前の時点である程度の調査が行われていて、メモリ確保数と解放数とがバランスしていることが確認されていた。よって、メモリリークの可能性は低いと考えられていた。
メモリリークではないとすると、メモリを大きく消費している箇所を調査する必要がある。メモリを大きく消費しているであろう箇所は、ある程度目星がついていて、スレッドのコメントデータ保持部分と、セッションの部分の二箇所である。今回は、真っ先に修正が可能であったスレッド部分の修正をしたのち、メモリ使用量のプロファイリング、最後にやや修正が難しいセッション部分の修正、という手順で改善を行った。

http://info.dwango.co.jp/rd/2010/10/post-3.html

実際の通信じゃないと再現しない現象とかありえるなぁ、負荷をツールで掛けても実際に現象が再現しないとか本当にあり得る話だ。

通信バッファと「ゾンビ化セッション」
最初に述べたとおり、メモリを大量に消費しそうな箇所は、スレッドと通信バッファであった。スレッドの方は最初の段階で最適化を既に行った。そこで続いては通信バッファに着目した。なお、通信バッファは std::string で実装されている。テスト環境のヒーププロファイラで調査したメモリ使用量の結果では、 std::string は std::deque に次いでおよそ二番目であった。
netstat で調査した結果、 Send-Q にデータが 10kB 以上溜まっているセッションが 1700 以上あった。一時的なものとは思えないほどの容量 (10kB 以上) が使用されていた。このようなセッションは、「プレイヤー側の OS のハングアップ」「LAN ケーブルの切断」「無通信状態が続き、プレイヤー側のネットワークの NAT テーブルが expire して送信できなくなったあとに、送信データが発生した」などによって発生しうる。これらのようなセッションを、以下「ゾンビ化セッション」と呼ぶ。
コメントサーバー側はセッション作成の際に KeepAlive タイマーを使用しており、デフォルトで二時間程度で強制的に切断される。そのため通信バッファの容量が無限に増え続けるということはない。しかし、「タイムアウトにより強制切断されて解放されるメモリの容量」が「新たなゾンビ化セッションが消費するメモリ容量」を上回ると、全体のメモリ使用量は増え続けることになる。
以上より、以下のような改善案を検討した。
1. netstat -tno で、ゾンビ化セッションがどれくらいあるのか確認する。ゾンビ化している可能性があるものは、 Timer 情報が unkn-4 となっているはずである。
2. 通信バッファの総容量を数分おきにログ出力し、メモリ使用量を監視する。
3. もしメモリ使用量が増え続けるならば、上記仮説は正しい可能性が高くなる。各セッションの通信バッファの使用量が大きなもの (100kB 程度) が発生したらログ出力する。
4. そのようなセッションが頻発するようであれば、強制的に切断するように修正する。

http://info.dwango.co.jp/rd/2010/10/post-3.html

日に数回の再起動が不要になるとかすごい改善!!!

まとめ
本稿では、ニコニコ広場コメントサーバーにおけるメモリ増大問題の顛末について述べた。コメントサーバーのメモリ使用量が、日に再起動を数回要求するほど増大していた。メモリリークの可能性はないという前提があり、ある程度の目星はつけつつ計測と修正を繰り返し、最終的に通信バッファの改善によって解決に至った。
今回の問題の根本的な原因の一つは、通信バッファであった。この結論に至るためには、メモリ使用量計測結果だけでは分からなかった。ヒーププロファイラの結果からは「おそらくメモリリークしていない」ということ程度しか分からず、間接的にしか役に立っていない。本番で稼働しているサーバーでヒーププロファイリングできたならば早く結論に辿りつけたかも知れないが、それは現実的には難しい。ソケットレベルの知識、今回の場合は TCP の KeepAlive などの知識から、総合的に判断しなければならなかった。ネットワークプログラミングは難しいのである。

http://info.dwango.co.jp/rd/2010/10/post-3.html

screenshot