こんにちは! フリーエンジニアの長瀬です。
みなさんはcacheを使っていますか?
cacheを上手に使用することができれば、簡潔にサイトを高速化することができます。
この記事では、cacheの使い方について
・cache(キャッシュ)とは
・Rails5でキャッシュを使用する準備をする
・ページキャッシング(ページ全体をキャッシュする)
・アクションキャッシング(特定のアクションをキャッシュする)
という基本的な内容から、
・フラグメントキャッシング(部分的にページをキャッシュする)
・モデルキャッシング(モデルでキャッシュする)
・保存されているcacheを削除する方法
といった応用的な内容についても解説していきます。
cache(キャッシュ)とは
cacheは今ではコンピューターの用語として定着していますが、元は「 〜を隠す」 という意味を持つ言葉です。
その語源の意味通り、cacheの役割は非常にシンプルです。
cacheでは一度読み込まれたデータをどこかに保存しておいて、次回からそのデータが保存してあるところからデータを取得することで毎回同じデータ読み込む無駄を回避します。
いちいち同じデータを取得する必要がなく、ページの読み込みなどのスピードが格段に速くなるためユーザーがより快適にアプリケーションを使用することができるようになります。
Rails5でキャッシュを使用する準備をする
では、早速チュートリアル形式で、キャッシュを体験してみましょう。
今回はrails5.1.4とruby2.4のバージョンで話しを進めていきます。
まずはコマンドプロンプトに以下のコマンドを打ち込んで、新しいrailsプロジェクトを立ち上げましょう。
rails new CacheTest -T
今回はテストを記述しないので、「-T」をつけてtestファイル群を作成しないように設定しています。
まず始めに、初期設定では開発環境のキャッシュは有効になっていないので、有効にする必要があります。
コマンドプロンプトに以下のコマンドを打ち込んで、開発環境でのキャッシュを有効にしてください。
rails dev:cache
[実行結果]
Development mode is now being cached.
これで開発環境でもキャッシュが有効になりました。
また、このコマンドでは、ディレクトリ直下のtmpフォルダ内にcaching-dev.txtというキャッシュ用のファイルも自動で作成されます。
それに伴って、config/environments/development.rbには以下の設定コードが記述されます。
# Enable/disable caching. By default caching is disabled. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true config.cache_store = :memory_store config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" } else config.action_controller.perform_caching = false config.cache_store = :null_store end
よく見るとif/elseの論理構造になっていて、さきほど紹介したtmp/caching-dev.txtがあるかないかによって条件分岐しています。
( if Rails.root.join(‘tmp/caching-dev.txt’).exist? )
tmp/caching-dev.txがある場合はキャッシュが有効になり、ない場合は無効になるように設定されているので、手動で有効/無効を切り替える必要がなくなります。
ページキャッシング(ページ全体をキャッシュする)
それでは、準備が整ったので、早速キャッシングを実装してみましょう。
ページキャッシングでは、ページの内容をすべてキャッシュに保存しておくことができます。
厳密に言うと、ページのすべてのHTMLをディレクトリ直下のpublicフォルダ内に保存します。
なので、ページを一度でも訪れたことのユーザはキャッシュの有効期限が切れていない限り、ビューとかレイアウトをもう一度取得する必要はありません。
しかし、認証されたユーザーだけがページを使用する場合や、ページの読み込み環境よって違う見え方をしている状態で、ページの全体をキャッシュに保存しておくのはあまりいい方法とは言えません。
なので、実はRails4の時点でページキャッシングはあくまでもgemの1つとして扱われるようになりました。
(元はRailsに標準で組み込まれていました。)
今回はページキャッシングを実装するので、Gemfileに以下のコードを加えてgemをインストールしましょう。
(中略) gem 'actionpack-page_caching' (中略)
記述が完了したら以下のコードで、Gemfileの内容から追加されたgemをインストールしてください。
bundle install
インストールが完了したら、設定ファイルに修正を加えて、キャッシュの保存先を指定する必要があります。
config/application.rbを開いて以下のコードを追加してください。
(中略) config.action_controller.page_cache_directory = "#{Rails.root.to_s}/public/page_cache" (中略)
これで、キャッシュの保存先を設定することができました。
これで一通り準備ができたので、コントローラーとビューを作成して、作成したページをまるごとキャッシュに保存する設定をしてみましょう。
今回はgenerateコマンドを使って、一気にファイルを作成しましょう。
以下のコードをコマンドプロンプトに入力してください。
rails g controller examples test
[実行結果]
create app/controllers/examples_controller.rb route get 'examples/test' invoke erb create app/views/examples create app/views/examples/test.html.erb invoke helper create app/helpers/examples_helper.rb invoke assets invoke coffee create app/assets/javascripts/examples.coffee invoke scss create app/assets/stylesheets/examples.scss
これで、今回必要なコントーラー、ビュー、ルーティングを作成できました。
キャッシュの設定はコントローラーに書いていきます。
app/controller/examples_controller.rbを以下のように修正してください。
class ExamplesController < ApplicationController caches_page :test end
caches_pageは先ほどインストールしたgemで追加されたヘルパーでtestアクションを丸ごとキャッシュに保存することを設定しています。
また、わかりやすくするためにapp/views/examples/test.html.erbを以下のように修正しましょう。
<h1>これはページキャッシュのテストのために、キャッシュに保存されるデータです。</h1>
それでは、この状態で
rails s
とコマンドプロンプトに入力してサーバーを起動してください。
そして、ルーティングの設定してある
http://localhost:3000/examples/test
(補足http://localhost:3000の箇所は各自の開発環境により読み替えてください。)
にアクセスしてください。
[実行結果]
一度読み込まれれば、次回以降はキャッシュに保存されたデータが呼び出されるようになります。
また、キャッシュを期限切れにする場合はコントーラーに以下のコードを追加してくたざい。
expire_page action: 'test'
expire_pageのヘルパーでアクションを指定してあげることで簡単にキャッシュを無効にすることができます。
アクションキャッシング(特定のアクションをキャッシュする)
アクションキャッシングはページを丸ごとキャッシュするという点では、ページキャッシングと同じですが、キャッシュを参照する前にActionPackを経由するので認証など、読み込みの前の段階での処理を組み込めます。
さらっとActionPackという用語がでてきましたが、これはRailsに標準で組み込まれているもので、リクエストが送られてきてからレスポンスを返すまでの処理を担当している部分の名称です。
Railsを触っている人なら、自然にいつもお世話になっています。
何はともあれ、アクションキャッシングでは前の段階で処理が実行できるという点で、ページキャッシングとは違います。
まずはページキャッシングと同様にアクションキャッシング用のgemを追加します。
Gemfileに以下を追加してください。
gem 'actionpack-action_caching'
そして、コマンドプロンプトに
bundle install
と入力してインストールしてください。
インストールができたら、さっそく実装してみましょう。
キャッシュに保存するにはcaches_actionというヘルパーを使用します。
さきほどと同じくコントローラーに記述していきます。
app/controller/examples_controller.rbを以下のように修正してください。
class ExamplesController < ApplicationController before_action :set_name, only: [:index] caches_page :test caches_action :index def test end def index end private def set_name @name = 'Samurai Engineer' end end
今回はindexというアクションを追加しました。
before_actionでset_nameのメソッドを指定して、onlyでindexアクションのみに適用しています。
このbefore_actionが使えるというところが、アクションキャッシングのポイントです。
この段階ではindexアクションに対して、まだビューやルーティングが設定してされていないので、設定しておきましょう。
config/route.rbを以下のように修正してください。
Rails.application.routes.draw do get 'examples/test' get 'examples/index' end
次はビューです。
app/views/examples/以下にindex.html.erbを追加して、以下のコードを入力してください。
<h1><%=@name%></h1>
それでは、ちゃんと読み込まれるか確認してみましょう。
同様にrails sでサーバーを立ち上げた後、
http://localhost:3000/examples/index
(補足http://localhost:3000の箇所は各自の開発環境により読み替えてください。)
にアクセスしてください。
[実行結果]
しっかりとbefore_actionで設定した値が読み込まれていることを確認できます。
また、ページキャッシュ同様にexpire_pageのヘルパーでキッシュを無効にできます。
フラグメントキャッシング(部分的にページをキャッシュする)
最後は部分的にページをキャッシュする方法を紹介します。
フラグメントキャッシングは、ページキャッシュやアクションキャッシュと違いRailsに正式に組み込まれています。
なので、いちいちgemを新たに追加する必要はありません。
それではまずは今回使用するモデルから作成していきましょう。
コマンドプロンプトに以下のコードを入力してください。
rails g model Book title:string rails db:migrate
これでBookモデルのマイグレーションがファイルが作成された後に、データベースにその内容が反映されました。
それでは、同じ流れで今回使用するコントローラーとビューとルーティングを設定していきましょう。
まずはapp/controller/examples_controller.rbを以下のitemアクションを追加してください。
(中略) def item @books = Book.all end (中略)
では、次にビューです。
app/views/examples/以下にitem.html.erbを追加して、以下のコードを入力してください。
<h1>Book list</h1> <% @books.each do |book| %> <table> <td> <%= book.title %></td> </table> <% end %>
コントローラーで定義した@booksを参照しています。
それでは、次にルーティングです。
config/route.rbに以下のコードを追加してくたざい。
get 'examples/item'
そして、最後にBookテーブルのtitleカラムに値をいれましょう。
rails consoleでコンソールを起動した後、以下のコードを入力してください。
Book.create(title:"侍Ruby入門") Book.create(title:"侍PHP入門") Book.create(title:"侍Perl入門") Book.create(title:"侍Java入門") Book.create(title:"侍Python入門")
これで、Bookのtitleに値が入りました。
それでは、部分的なキャッシュの設定をしていきましょう。
app/views/examples/item.html.erbを以下のように修正してください。
<h1>Book list</h1> <% @books.each do |book| %> <table> <% cache book do %> <td> <%= book.title %></td> <% end %> </table> <% end %>
cacheメソッドの後に名前を指定することで、キャッシュに保存される際の名前を設定できます。
(今回はbookです。)
それでは、確認してみましょう。
rails sでサーバーを立ち上げた後、
http://localhost:3000/examples/item
(補足http://localhost:3000の箇所は各自の開発環境により読み替えてください。)
にアクセスしてください。
[実行結果]
始めに読み込まれた時点でキャッシュが実行され、次回以降はcacheメソッドで設定した部分はキャシュから取得されます。
また、renderメソッドを使えばオプションとして簡単にキャッシュを指定できます。
app/views/examples/item.html.erbを以下のように書き換えてみてください。
<%= render :partial => "book", cached: true %>
このようにcached:trueとすることで簡単にキャッシュを実装できます。
partialには先ほどのコードを入力します。
app/views/examples/以下に_book.html.erbを作成して以下のコードを入力してくたざい。
<% @books.each do |book| %> <table> <% cache book do %> <td> <%= book.title %></td> <% end %> </table> <% end %>
これで、同じ箇所がキャッシュとして保存されます。
また、フラグメントキャッシュでキャッシュを無効にしたい場合はexpire_fragmentのヘルパーを使用します。
例えば、今回の例だとコントローラーに以下のように記述します。
(中略) def item @books = Book.all expire_fragment(@books) end (中略)
モデルキャッシング(モデルでキャッシュする)
モデルでキャッシュを実装することで、毎回データをデータベースから取得する代わりにキャッシュに保存されたデータを用いるようになります。
モデルでキャッシュをするにはfetchメソッドを使用します。
先ほど作成したapp/models/book.rbに以下のようにクラスメソッドを追加してください。
class Book < ApplicationRecord def self.cache_all Rails.cache.fetch("books"){Book.all} end end
これで、Book.cache_allメソッドを使用したときに、”books”という名前で、Book.allの値をキャッシュできます。
では、さっそくコントローラーで実装してみましょう。
先ほど作成したitemアクションを修正します。
def item @books = Book.cache_all end
Book.allで、直接データベースから値を取得する代わりに作成したBook.cache_allを使用しています。
このように設定すれば、データベースからデータを取得した時点でキャッシュに保存して使用できようになります。
保存されているcacheを削除する方法
cacheを削除するには、deleteとclearのメソッドが用意されています。
Rails.cache.delete('キャッシュの保存名')
deleteでは引数に渡した’キャッシュの保存名’に該当するキャッシュがピンポイントで削除されます。
なので、特定のキャッシュを削除する場合はこちらを使用します。
例えば、今回の例ですと
Rails.cache.delete('books')
と記述すれば、’books’として保存されているキャッシュは削除されます。
一方で
Rails.cache.clear
clearでは、アプリケーションで保存されたすべてのキャッシュを削除します。
なので、使用には注意が必要です。
必要なキャッシュも丸ごと削除される可能性もあるので、よく考えてから使用するようにしましょう。
まとめ
いかがでしたでしょうか?
この記事では、cacheの使い方を解説しました。
・ページキャッシングはページ全体をキャッシュ、別途gemのインストールが必要
・アクションキャッシングはページ全体をキャッシュ、実行前の処理が可能、別途gemのインストールが必要
・フラグメントキャッシングは部分的にページをキャッシュ、Rails標準
・モデルキャッシングはデータベースからのデータをキャッシュ、Rails標準
それぞれ使用目的や、使用する場合が異なるので、キャッシュを使用して高速化を実装する場合は、適材適所でキャッシュを組み込んでいくようにしてください。
もしcacheの使い方について忘れてしまったらこの記事を確認してくださいね!