現在作っているプロジェクトの高速化のために、キャッシュの利用を考えたのですが、本番環境としてherokuを検討しているので、herokuでmemcachedが使える方法を模索していました。ちなみにherokuではmemcachedのadd-onが2つあります。
今回は料金の安い方のMemCachierを使う事にしてみます。
Rails3,4での導入方法は、Heroku DevCenterに書かれています。
Gemfileに以下を追加。
gem 'memcachier' gem 'dalli'
そしてインストール。
bundle install
そしてcache_storeにdalliを設定します。
config.action_controller.perform_caching = true config.cache_store = :dalli_store
開発環境(ローカル)で試したい場合はdevelopment.rbにproduction.rbと同様の設定を行いましょう。
なお、ローカルでmemcachedがインストールされていない場合はインストールしましょう。Mac OSXの場合、homebrewで入れられます。
brew install memcached
memcachedが動いているかどうかの確認。
ps ux | grep memcached
memcachedの自動起動・停止はこちら(OSX)
launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist # 起動 launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist # 停止
これといって特に設定しないでも動く事は確認できましたが、手動でmemcachedにアクセスしたい場合は、telnetで接続してみましょう。
telnet localhost 11211
memcachedのコマンドはこちらを参考にしましょう。
memcachedコマンド一覧(Life with IT)
memcachedに値をセットする場合、領域を確保してからデータを入れることになります。
set foo 0 0 3[Enter] 123[Enter] => STORED get foo => VALUE foo 0 3 => 123 => END
とりあえずこれでmemcachedの動作は確認できました。
私の場合はフラグメントキャッシュ(ページの一部をキャッシュ)をやりたかったので、それの実験をしてみましたが、ちょっとハマってしまったのでメモ書きを残します。
フラグメントキャッシュは簡単です。例えばサイドバーに広告を出していたとして、それは別に頻繁に更新されないのだとしたら、キャッシュしておきたいでしょう。こんな感じでやります(テンプレートエンジンはslim)。
- cache :top_ad_area do div.sidebar ul - @ads.each do |ad| = link_to ad.name, ad_path(ad)
これで、初回のアクセスでキャッシュを作成して、次回からはキャッシュが使われます。しかし、@adsという変数はコントローラーから引き継がれています。@adsを作るためにDBにアクセスしていたら、キャッシュの旨味がないので、これを抑止します。
before_action do unless fragment_exist? :top_ad_area @ads = Ad.all end end
これで大丈夫だと思ったら、毎回毎回@adsが生成されていました…。
ログを見てみると、作られたキャッシュ側にはダイジェスト文字列が割り当てられていました。
controllerのfragment_exists?での確認対象: views/top_ad_area
viewのcache :top_ad_area での確認対象: views/top_ad_area/f6da12ef………
この影響でfragment_exists?は全然ヒットしてなかったのです。
この場合、view側のcacheのところで、ダイジェスト文字列を作らないように抑制します。
- cache :top_ad_area, skip_digest: true do div.sidebar ul - @ads.each do |ad| = link_to ad.name, ad_path(ad)
これでめでたくキャッシュにヒットして、クエリの抑制ができるようになりました!
参考ページ
RailsのFragment Cache(元祖ワシ的日記 4.0)
また、デプロイしたときにキャッシュをクリアするようにしておきましょう。
アプリケーションサーバとしてunicornを使っている場合は、以下を追記します。
after_fork do |server, worker| if defined?(ActiveSupport::Cache::DalliStore) && Rails.cache.is_a?(ActiveSupport::Cache::DalliStore) Rails.cache.reset # SessionStoreにDalliを使っていたらこちらも有効にする #ObjectSpace.each_object(ActionDispatch::Session::DalliStore) { |obj| obj.reset } end end
herokuにデプロイしてみましたが、エラーにならずに表示できているので大丈夫だと思います。