Webアプリケーションを運用する中で「この画面が遅いね、もっと動作を速くしたいね」というシーンが度々ありました。 その都度、その時思いつくさまざまな対応をしてきたので、それを思い出しながら雑にメモしておきます。
自分は主にRailsでアプリケーション開発をしているので、その前提で「遅い画面を産まないためにチェックすること」という視点で書き残します。 (思いついたら追記します)
- Railsのキャッシュ機構を活用できそうか
- N+1問題が発生してないか
- 適切にindexが張られているか
- 巨大なテーブル同士のJOINや不要なJOINがないか
- Materialized View が活用できそうか
Railsのキャッシュ機構を活用できそうか
Rails のキャッシュ機構 - Railsガイド を頭に入れておきましょう。 どれも便利です。
あと、オプションのところに記載されている race_condition_ttl
も便利です。使いましょう。
マルチプロセスで同じエントリが同時に再生成されてキャッシュが無効になる(dog pile効果とも呼ばれます)ときに発生する競合状態を防止するのに使います。このオプションは、新しい値の再生成が完了していない状態で、無効になったエントリを再利用してよい時間を秒で指定します。
N+1問題が発生してないか
初歩的な話ですが preload
と eager_load
を適切に使っているかを確認します。
この辺りの挙動については2014年の記事ですが ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い - Qiita が分かりやすいです。
個人的には includes
はコードリーディングしたときに挙動を想像しにくいと感じるのであまり積極的には使いません。*1
目的を意識しながら preload
と eager_load
を使い分けて実装するようにしています。*2
N+1問題の検知については、 bullet を導入して、CIの実行時に bullet がテストコードでN+1問題の発生を検知したらCIをfailさせるように設定しておくと便利です。
適切にindexが張られているか
クエリの実行計画をみます。EXPLAIN
しましょう。
巨大なテーブル同士のJOINや不要なJOINがないか
巨大なJOINがいくつもある場合は、テーブル構成を見直すきっかけになります。
また、古い機能の名残りでいまは必要のないJOINが含まれていた、といった場合もありそうなので合わせてチェックしましょう。
Materialized View が活用できそうか
例えばランキング表示などために既存のテーブルをいくつか組み合わせて集計する必要がある時など、画面へのアクセスのたびに集計用の重いクエリが発行されてしまう場合があります。そんな時は Materialized View を作って集計結果を記録しておくと便利です。ただし、即時性が必要とされない画面だけで使える手段ですね(1時間毎に最新の集計結果になっていればOK、などであれば適していそう)