-
Notifications
You must be signed in to change notification settings - Fork 9
Building a rails 3 application with memcache
キャッシュをあなたのWebアプリケーションに追加することはに強大なパフォーマンスの改善になるかもしれません。複雑なデータベースのクエリの結果、高負荷な計算、または外部リソースへの遅い呼び出しは、計算量O(1)の早い参照を経由してアクセスされる単純なキーバリューストアになり得ます。
このチュートリアルは、簡単なRails3.2のアプリケーションを作成とHerokuへのデプロイ、そして高負荷なクエリをキャッシュするためにMemCachierアドオンを利用する過程を追います。
[デモ用Railsアプリケーション](https://github.com/memcachier/examples-rails3-heroku)はGithubで利用可能です。デモの実行中の物は[こちら](http://memcachier-examples-rails3.herokuapp.com/)で確認できます。
- 基本的なRuby/Railsの知識。インストールされているバージョンであるRuby1.9.2,Rubygems,Bundler,そしてRails3を含みます。
- 基本的なGitの知識
- Herokuのユーザアカウントを持っている事。こちらから無料で素早く取得できます。
アプリのスケルトンを生成するために、rails
コマンドを実行します:
:::term
$ rails new memcache-example
$ cd memcache-example/
次にデータベースです。Gemfileの中で、下記にある行 :
:::ruby
gem 'sqlite3'
を次のように変更します :
:::ruby
group :development do
gem 'sqlite3'
end
group :production do
gem 'pg'
end
これはアプリケーションがプロダクション環境でデータベースにPostgresを使う事を宣言しています。
次に以下を実行します。:
:::term
$ bundle install --without production
これは指定されたgemをインストールしGemfile.lock
ファイルを生成します。
--without production
オプションは、(プロダクション環境でしていされている)pg gemがローカルでインストールされるのを防ぎます。
このプロジェクトをGitリポジトリとし、変更をコミットします。:
:::term
$ git init
$ git add .
$ git commit -m "first commit"
heroku
コマンドを使用して、新しいHerokuアプリケーションを追加します。
:::term
$ heroku create
そしてHerokuにデプロイします。
:::term
$ git push heroku master
MemCachierの記事の中で言及している通り、アドオンとdalli
gemのインストールが必要になります。オプションであるmemcachier
gemも推奨されます。
ターミナルにて以下を実行します :
:::term
$ heroku addons:add memcachier:dev
シンプルな設定を助けるgem、dalli
、memcacheのクライアントライブラリ、そしてmemcachier
を含める形でGemファイルを変更します。
:::ruby
gem 'dalli'
gem 'memcachier'
そしてRailsのデフォルトキャッシュの設定を、dalli
が提供するキャッシュストアを利用する形に変更します。config/environments/production.rb
に下記を含めます :
:::ruby
config.cache_store = :dalli_store
このサンプルがどのように動くのかを簡単に見るために、一時的に組み込まれているキャッシュを向こう化します。以下の行をコメントアウトします :
:::ruby
# config.action_controller.perform_caching = true
Railsのscaffoldジェネレータを使って、名前とメールアドレスが含まれる簡単な名簿を保存、閲覧できるインターフェースを生成します。
:::term
$ rails g scaffold contact name:string email:string
$ rake db:migrate
config/routes.rb
を編集し、contacts#index
をルートとして設定します。
:::ruby
root :to => 'contacts#index'
そしてpublic/index.html
を削除します。
変更をコミットし、Herokuへプッシュ、そして遠隔にあるデータベースをマイグレートするために以下のコマンドを使用します。
:::term
$ heroku run rake db:migrate
これでheroku open
を使って連絡先のリストを見る事ができるようになっているはずです。"New Contact"リンクを辿り、いくつかの連絡先を追加してください。
ContactsController
の中にあるコードはこのようになっています。:
:::ruby
def index
@contacts = Contact.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @contacts }
end
end
/contacts
と要求される時はいつも、このindex
メソッドが実行され、Contactsテーブルの中にある全てのレコードを取得するデータベースのクエリが実行されます。
テーブルが小さくて、リクエストの量が低いときにはこれはそんなに問題にはなりません。しかし、ユーザ数とデータベースが育つ程に、このようなクエリはあなたのアプリケーションに影響を与える可能性があります。このページに訪れたときにいつもデータベースのクエリ実行されないように、Contact.all
の結果をキャッシュしてみましょう。
app/models/contact.rb
に以下のメソッドを追加します。 :
:::ruby
def self.all_cached
Rails.cache.fetch('Contact.all') { all }
end
app/controllers/contacts_controller.rb
にある以下の部分 :
:::ruby
@contacts = Contact.all
を以下の通りに変更します。 :
:::ruby
@contacts = Contact.all_cached
一緒にいくつかの統計情報もIndexページに載せてみましょう。以下の行をapp/controllers/contacts_controller.rb
の中のindex
メソッドに追加します。 :
:::ruby
@stats = Rails.cache.stats.first.last
そして、以下のタグをapp/views/contacts/index.html.erb
の下に追加します :
:::html
<h1>Cache Stats</h1>
<table>
<tr>
<th>Metric</th>
<th>Value</th>
</tr>
<tr>
<td>Cache hits:</td>
<td><%= @stats['get_hits'] %></td>
</tr>
<tr>
<td>Cache misses:</td>
<td><%= @stats['get_misses'] %></td>
</tr>
<tr>
<td>Cache flushes:</td>
<td><%= @stats['cmd_flush'] %></td>
</tr>
</table>
結果をコミットし、Herokuにプッシュします。/contacts
ページを再読み込みすると、"Cache misses: 1"という表示が見えるでしょう。これはContact.all
のキーを取得しようとしたけれども存在しなかったために起きています。再度再読み込みをすると"Cache hits: 1"と表示されるでしょう。今回は、前のリクエストによってContact.all
のキーが保存されたため、キーが存在していたのです。
Herokuのコンソールからキャッシュを消す事で、この現象を再度確認することができます。
:::term
$ heroku run console
>> Rails.cache.clear
今Contact.all
はキャッシュされるようになりましたが、テーブルに更新があったら何が起こるでしょうか?試しに新しい連絡先を追加して、リストしている画面に戻ってみてください。きっと新しい連絡先が表示されていないはずです。Contact.all
がキャッシュされたために、古い値がまだ提供されつづけているのです。そこで何か変更が会った場合に、キャッシュされた値を破棄する手段が必要になります。これはContact
モデルの中のフィルターで実現する事が出来ます。
以下のコードをapp/models/contact.rb
に追加します :
:::ruby
after_save :expire_contact_all_cache
after_destroy :expire_contact_all_cache
def expire_contact_all_cache
Rails.cache.delete('Contact.all')
end
これらの変更をコミットし、Herokuに追加します。これで、あなたが連絡先を保存(作成及び変更)または削除するときに、Contact.all
のキャッシュのキーが削除されるようになりました。あなたがこのような連絡先の更新をしたあと/contacts
に戻ってきたときには、いつも"Cache misses"の数字が1に繰り上がっているはずです。
上記の例は、どのように明示的にキャッシュを取得し破棄するかを説明してきました。便利な事に、Railsにはこのような機能が多く組み込まれています。もしshow
のアクションの結果をキャッシュしたいと思ったならば、app/controllers/contacts_controller.rb
に以下の行を追加します :
:::ruby
caches_action :show
適切な破棄のために、以下の行をcontacts_controller.rb
内のupdate
とdestroy
メソッドの両方に追加します :
:::ruby
expire_action :action => :show
アクションキャッシュはページキャッシュにとても似た形でビューとオブジェクトを保存します。しかし認証の様にbeforeフィルタが適応されるように、リクエストは実際にアプリケーションスタックを叩きます。
更に読み進めたいという方のために、Rails Guide on CachingはRailsに組み込まれているアクション、フラグメント、そして他の種類のキャッシュに関するすばらしい情報を持っています。
このチュートリアルで作ったアプリケーションの完全なソースは、自由にGitHubからダウンロードすることが可能です。