4 Simple Steps to Detect & Fix Slow Rails Requests
2016年更新:これらの手順を自動化するScout App Monitoring をリリースしました。
マルコム・グラッドウェルは「一瞬の判断」について書かれた著書「Blink」の中で、シカゴのクック郡病院救急部が胸痛診断プロセスをどのように変えたのかについて触れています。
ブレンダン・レイリー博士は、患者が心臓病を患っているか否かを判断するシンプルな検査を開始しました。4つの質問と心電図の結果を組み合わせただけのものです。このシンプルな検査は、それまで病院のスタッフが心臓発作を患っていない患者を特定するために行っていた一連の質問に比べ70%優れており、かつ心臓発作を患っている患者を特定するのにも20%近く優れていました。
患者の症状に関する多すぎる情報はしばしば間違った診断につながっていました。医師たちを本質的な問題から遠ざけてしまっていたのです。
Railsアプリケーションのパフォーマンスの問題をデバッグしようとする開発者に、これと同じような傾向をみることが度々ありました。明確な原因ではなく、ずれたところに注目してしまっているパターンです。これは、深く、詳細なRailsのモニタリングアプリケーション(例えば、リクエストごとにコントローラーからデータベースレベルまでのベンチマークを計測するような)の必要性を感じたことがない理由の一つです。
ほとんどの場合、パフォーマンスの問題はRailsフレームワークとは無関係です。(また、2005年にRailsアプリケーションの構築を開始してから、多くの問題が解決されてきています。)
問題の大半がデータベース層で分離されているのに、リクエストサイクル全体をベンチマークしてしまうのはなぜでしょうか。データベースを除外すると、単一のリクエストをベンチマークすることができます(非常に優れた無料ツールがあり、後述します)が、その他のツールは無関係な情報により考えを曇らせてしまうため、純粋に不要だと考えています。
アプリケーションにおいて避けたい根本的な症状は、リクエストの遅さです。低速なリクエストを解析するためのScout プラグインは250近くのインストール実績がありますので、我々以外にも利用している人が多くいると言えます。
First, it’s probably not Rails
インターネット上で言われていることに反し、アプリケーションを遅くしているのは恐らくRailsそのものではありません。Highgroove Studiosチームの内部調査を実施し、パフォーマンスの問題がどこで起きているのか、根本原因はなんなのか、について確認しました。
データベース層は、他のどの層に比べて、問題が山積しています。実際、パフォーマンスの問題はどの言語のどのフレームワークでも起こりえます。インデックスの欠落であったり、正しい結合が行われていなかったり、メモリにレコードをロードしすぎていたり、繰り返し処理(#map, #each)によって操作するレコードが多すぎたり、多くの言語でメモリリークが発生したりといった問題があります。
パフォーマンスの問題があること自体は悪いことではありません、アプリケーションは成長を続けます。しかし、それらが即座に修正されていないのは悪いことと言えます。
1. Monitor for slow web requests
- 低速なWebリクエストの監視
まず、できるだけ早く低速なWebリクエストに気づきたいものですね。我々はScout の Slow Rails Requestsを利用して低速なリクエストについてリアルタイムで通知を受けています。その理由は以下のとおりです。
- リリースサイクルは非常に速いため、新しいリリースの副作用にできるだけ早く気づく必要がある
- 週次でログの解析を行うことは可能だが、自動的に実行・完了されないタスクを脇においた上で、通知への対応を行うことは、はるかに簡単である
- クライアントよりも先に低速なリクエストを検知したい
このプラグインがインストールされるとすぐに低速なリクエストが通知されるようになります。では、アプリケーションのパフォーマンスに影響を与える可能性のある主なメトリックスを見ていきましょう。
2. Monitor Server Load
- サーバー負荷の監視
ほとんどのnixサーバー(UNIX系のサーバー)はサーバー負荷と呼ばれるサーバーの状態を測定します。通常、サーバー負荷は3つの異なる期間の負荷平均で示されます。
サーバー負荷とは本質的にリソースが利用可能になるまでのキューに入れられたプロセス数を大まかな目安としています。このリソースとは、一般的にCPU時間をさしますが、メモリやスワップ領域、ディスクなどの他の多くの要素が含まれることもあります。数値が小さいほど、システム全体の状態と応答性は良いと判断できます。
3つの平均値とは、直近の1分間、5分間、15分間をさします。これらの平均値を使用すると、実際にサーバーがどの程度ビジーな状態であるかを確認することができます。
サーバー上で“top” コマンドの出力結果を見ると、このサーバーはまったくビジーでないことがわかります。実際このサーバーの現在の負荷は3つ全ての平均値で0.00です。これは理想的で、プロセスを処理するのに待機しているのに理想的な状態のサーバーであると言えます。
負荷が特定のしきい値(おそらく 3.0)になると、プロセスの実行速度が遅くなり Railsアプリケーションが応答しなくなってしまうことがよくあります。通常、ScoutのLoad Average プラグインは負荷が3.00を超えるとアラートを上げるようになっています。
Why
なぜ?(サーバー負荷の監視が大切なのか)
低速なWebリクエストは急激な負荷の上昇を引き起こす可能性があります。または、バックグラウンドのジョブが多くのCPUを使用していたり、大量のリクエストが送信されてきていたりするため速度低下が起こっているということなどもあります。負荷をトラックすることで問題解決につながります。
3. Monitor Memory Usage
- メモリ使用量の監視
メモリについて、Railのセットアップで、よく監視対象となるものが2つあります。
- Mongrelプロセスと関連プロセスの(Ferretサーバーのような)メモリ使用量
- システムのメモリ使用量。最も重要なのはスワップ領域の使用量
プロセスがレジデントメモリ(実際にプロセスに割り当てられている物理メモリ)を利用すると、同様に仮想メモリの使用量も段階的に上がっていくことを知っておくことは大切です。プロセスは実際にはこの”仮想メモリ”をシステムの物理メモリよりも多く使用するように見えます。ほとんどのオペレーティング・システムはメモリ内ページングとリソースの共有を管理できるため、これは、まったくもって正常な動作ですが、この仮想メモリが、スワップ領域を使用して、ハードドライブを物理メモリかのように使用し、ディスクにページングするプロセスが開始されると、速度低下、悪くするとアウトオブメモリーを引き起こします。
こんな風に考えてみてください。もしレストランで働いていたとして、大量の食器(プロセス)と5台のとても速い食洗機(レジデンス/物理メモリ)、それとかなり低速な5台の食洗機(ハードドライブ/スワップ領域)が与えられたとします。その場合、全ての食器を速い食洗機で洗えるように最適化を試みることがベストです。送られてくる食器を全て処理しきれないときのみ、本当に必要なときにだけ、低速な食洗機を利用します。
多くのRailsアプリケーション(アプリケーション自体、またはサードパーティーのライブラリ)はメモリリークに苦しめられます。サーバーがメモリを使うほど、レジデントメモリも仮想メモリの使用量も増加します。仮想メモリのスワップ領域としてハードドライブが利用されるようになり、物理メモリに比べはるかに低速になります。これによりシステム全体のパフォーマンス、つまり全てのリクエストが大幅に低下する可能性があります。
Memory Profilerプラグインによって、Mongrelのプロセスが閾値(通常約100MB)を超えたことが検知された場合、及びスワップ領域の使用率が閾値(通常60%)を超えたことを検知した場合は、Process Usageプラグインを介してアラートがあげられます。
Why
なぜ?(メモリ使用量の監視は必要か)
多くの場合、これは簡単に解決できる問題です。リークを見つけるのが難しい場合(通常は難しいですね)、再起動をスケジュールすることができます。スワップ領域を常に多く使用しているようであれば、おそらくは、より多くのメモリが必要ということになります。(開発を行う時間に比べれば微々たるものです)
4. Fixing slow requests
- 低速なリクエストの修正
ということで、Scoutは低速なWebリクエストについてアラートを送ってきます。さて、次は何をすればよいのでしょうか?
Install the Query Reviewer Rails Plugin
Query Reviewer Railsプラグインのインストール
前述の通り、パフォーマンス問題の殆どがデータベースに起因しており、Query Reviewer プラグインはMySQLに関する問題を検知し、リクエストサイクル全体をベンチマークしてくれるという素晴らしい仕事をします。このプラグインの主な特徴としてはクエリ情報が直接画面に埋め込まれ確認できることです。
The Optimization Process
最適化プロセス
Scoutで低速なWebリクエストを識別する際は以下の手順で行います。
- Scoutにログインしプラグインにより提供されている、低速なRailsのリクエスト、CPU負荷、メモリ使用率のデータを確認します。CPU負荷が高い場合、Mongrelプロセスのメモリ使用率が高い場合、諏訪府領域の使用率が不当に高い場合、他の問題が低速なリクエストの原因となっている可能性があります。Mongrelプロセスを再起動するか、他のバックグラウンドで動いているジョブの確認を行った上で、再度リクエストを行います。
- ローカル環境で、低速なリクエストを実行し、事象が再現できるか確認します。MySQL用の Query Reviewerプラグインが有効になっていることを確認します。
- MySQL用のQuery Reviewerプラグインに提供された情報を確認し、SQLクエリを調整します。手順2と3をパフォーマンスが共用範囲となるまで繰り返します。
Summary
まとめ
多くの人がRailsスタックをトレースしパフォーマンスの問題に対応するために多くの時間を無駄にしていますが、その原因は多くの場合単純なところにあります。Railsスタックを調べる前に、まずはわかりやすい場所から調べてみてください。
リンク: