asin: 4797363827
title: “[Rails高速化] ページキャッシュ、N+1対策、SQLチューニング”
category: Rails

🎳 tags: [Rails, Ruby, Gem]

Cookpadではユーザーへのレスポンスタイム 200ms/reqを目標にしている』に感銘を受けて書き始めたこの記事ですが、『パフォーマンス・チューニングやオススメGem in 「Rails勉強会@東京 第88回」』でいろいろ教えてもらったり、最近関わっているサイトのリニュアールで試行錯誤したので、忘備録も兼ねて記事をアップデートします!


🏀 目次

(1) N+1問題の対策

(2) Railsのキャッシュについて

(3) 開発中ならrack_mini_profiler

(4) 運用中なら断然NewRelicがおすすめ

(5) mysqlの簡単チューニング

🐰 (1) N + 1問題の対策Gem Bullet

Bulletの導入手順

RailsのActive Record(findやwhere)は便利ですが、DB(SQL)の側から見ると非効率的なクエリを発生させることがあるようです。これを監視して警告を出してくれることで有名なGemが『Bullet』です。

まずは、bulletをインストールします。Gemfileに以下を追記してbundle installを実行。

group :development do
# N+1問題の警告を出力
gem "bullet"
end

続いてconfig/environments/development.rbに以下を追記。

AppName::Application.configure do
config.after_initialize do
Bullet.enable = true # Bulletプラグインを有効
Bullet.alert = true # JavaScriptでの通知
Bullet.bullet_logger = true # log/bullet.logへの出力
Bullet.console = true # ブラウザのコンソールログに記録
Bullet.rails_logger = true # Railsログに出力
end
end

これでブラウザで開発サイトを普通に閲覧していると、N+1問題が発生するページではアラートを出してくれます。

複数のテーブルを含むincludesについて

Bulletでは関連テーブルを呼び出すようなSQLを検知した場合に、複数のテーブルを含むincludesをするようにアラートが出ます。たとえば、ブログ(Blog)に記事(Articles)があり記事に複数の画像(Images)があるようなモデルを考えます。

Blog => Articles => Images

この場合には、Active Recordで次のようなコードを書けば複数テーブルを含むincludesを指定できます。

Blog.includes(articles: [:images])

頭のなかだけで考えただけですので、間違っている可能性もありますが、このようにハッシュ&Array&Symbolでうまく呼び出してあげる必要があります。こちらの記事『【Ruby On Rails3】 複数のテーブルのincludesを書く! | approad』を参考にさせていただき、うまく組むことができました!

🐯 (2) Railsのキャッシュについて

フラグメントキャッシュ(RussianDollCaching)

Rails 4になって、メインのキャッシュ機能は『フラグメントキャッシュ(RussianDollCaching)』になったようです。

http://o.inchiki.jp/obbr/201

フラグメントキャッシュの詳細な説明は、『Rils4で Russian Doll Caching を楽しむためのまとめ [俺の備忘録]』がわかりやすくてお勧めです!

ちなみに、恥ずかしながら最近フラグメントキャッシュを使うようになりましたが、cache modelid-updated_atという情報を付加してくれます。(modelに変更があったら自動的にキャッシュがアップデートされます)かなり気に入っています^^

ページキャッシュについて

Rails 4からは、ページキャッシュは別のgem『rails/actionpack-page_caching』として提供されるようになったようです。

http://www.rubytutorial.io/page-caching-with-rails-4

こちらの記事『Page caching with Rails 4』は英語ですが、シンプルでわかりやすい説明です。ページキャッシュはRailsを介さずにApacheと直接やりとりができるため、レスポンスが高速になるので、もし使いやすいサイトであれば積極的にトライしてみるのもありだと思います!

🗻 (3) 開発中ならrack_mini_profiler

現状、Railsのプロファイラで一番メジャーなのは、『MiniProfiler/rack-mini-profiler』です。

ViewのレンダリングやActive Recordの問い合わせにかかっている時間などを計測して教えてくれます。

導入方法もいたって簡単です! Gemfileに次のコードを追加して、コマンドラインでbundle installを実行。

gem 'rack-mini-profiler'

あとは、ブラウザで開発サイトを普通に閲覧していると、小さく速度を計測して表示してくれます。ドリルダウンで詳細な問い合わせ内容もすぐにわかってボトルネックの部分を調べることができます!

🍮 (4) 運用中なら断然NewRelicがお勧め

http://www.engineyard.co.jp/blog/2013/new-relic-night/

実運用中のサイトのチェックであればNewRelicがやはりお勧めです。上のサイトは去年開催された『New Relic Nightを開催しました。(動画&スライド)』の内容です。運用ノウハウのつまったスライドが多数あります。

以前『Rails勉強会@東京 第88回』に参加させていただいた際にも次のようなメリットがあるというのを教えていただきました!

* アプリケーションの監視に最適なツール
* アクション => メソッド、sqlが遅いまでチェックできる
* アクションをひとつひとつチェック => 定義・構造がおかしいか探す => レンダリングの改善
* サイト全体平均より、重要なページを計測して改善をすべき
* js側のチェックも出来る

🗽 (5) MySQLの簡単チューニング

Railsで作ったサービスの速度改善方法について教えて下さい - QA@ITの中にあるMySQLの簡単なチューニングをやってみたところ、僕の環境でも体感で速度が改善しました。

まず、vim my.cnfの中の次の設定を変更します。

innodb_buffer_pool_size = # サーバのメモリ70〜80%
innodb_log_file_size = # 100-500MB程度
innodb_flush_log_at_trx_commit = 2

次にMySQLの

innodb_log_file_size
を変更するために、MySQLにログインして次のコマンドを実行します。

mysql> SET GLOBAL innodb_fast_shutdown=0;

次にMySQLをストップします。

/etc/init.d/mysqld stop
# mac/homebrewの場合は、mysql.server stop

そしてトランザクションファイルを削除(もしくは移動)。

rm /var/lib/mysql/ib_logfile0
rm /var/lib/mysql/ib_logfile1
# mac/hoebrewの場合のデフォルトは:
# rm /usr/local/var/mysql/ib_logfile0
# rm /usr/local/var/mysql/ib_logfile1

MySQLを起動します。


トランザクションログファイルが作成されていれば成功です。

🎉 参考リンク

N+1問題などを監視してパフォーマンスを改善するRailsプラグインBullet|WEBデザイン Tips

Railsの画面生成を10倍高速化する方法 - 世界線航跡蔵

MySQLの「innodb_buffer_pool_size」と「innodb_log_file_size」の設定 – FlatLabs

🐹 変更来歴

13/04/17 10:00 Bulletの仕様変更により、development.rbの設定を変更

13/05/20 17:35 MySQLの簡単チューニングを追加

14/01/26 19:05試行錯誤した結果にもとづいていくつか書き直し

📚 おすすめの書籍