Rails的Cache分四種:
1,Page Cache - Fastest
2,Action Cache - Next Fastest
3,Fragment Cache - Least Fastest
4,ActiveRecord Cache - Only available in Edge Rails
下面一一介紹上面四種Cache以及Rails如何使用memcached
一、Page Cache
如果開發階段要使用cache,則需要先設定好config/environments/development.rb:
Java代碼
- config.action_controller.perform_caching = true
而production環境下預設是開啟cache功能的
Page Cache是Rails中最快的cache機制,使用Page Cache的前提一般為:
1,需要cache的page對所有使用者一緻
2,需要cache的page對public可通路,不需要authentication
Page Cache使用起來很簡單:
- class BlogController < ApplicationController
- caches_page :list, :show
- def list
- Post.find(:all, \:order => "created_on desc", :limit => 10)
- end
- def show
- @post = Post.find(params[:id])
- end
這樣我們就對BlogController的list和show頁面進行了緩存
這樣做的效果是第一次通路list和show頁面時生成了public/blog/list.html和public/blog/show/5.html這兩個html頁面
對于分頁情況下的cache,我們需要把url的page參數改寫成"blog/list/:page"這種形式,而不是"blog/list?page=1"這種形式
這樣cache的html頁面即為public/blog/list/1.html
當資料更改時我們需要清除舊的緩存,我們采用Sweepers來做是非常不錯的選擇,這把在BlogController裡清除緩存的代碼分離出來
首先編輯config/environment.rb:
- Rails::Initializer.run do |config|
- # ...
- config.load_paths += %w(#{RAILS_ROOT}/app/sweepers)
這告訴Rails加載#{RAILS_ROOT}/app/sweepers目錄下的檔案
我們為BlogController定義app/sweepers/blog_sweeper.rb:
- class BlogSweeper < ActionController::Caching::Sweeper
- observe Post # This sweeper is going to keep an eye on the Post model
- # If our sweeper detects that a Post was created call this
- def after_create(post)
- expire_cache_for(post)
- # If our sweeper detects that a Post was updated call this
- def after_update(post)
- # If our sweeper detects that a Post was deletedcall this
- def after_destroy(post)
- private
- def expire_cache_for(record)
- # Expire the list page now that we posted a new blog entry
- expire_page(:controller => 'blog', :action => 'list')
- # Also expire the show page, in case we just edit a blog entry
- expire_page(:controller => 'blog', :action => 'show', :id => record.id)
然後我們在BlogController裡加上該sweeper即可:
- cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]
我們可以配置cache的靜态html檔案的存放位置,這在config/environment.rb裡設定:
- config.action_controller.page_cache_directory = RAILS_ROOT + "/public/cache/"
然後我們設定Apache/Lighttpd對于靜态html檔案render時不接觸Rails server即可
是以Page Cache就是最快的Cache,因為它不與Rails server打交道,直接load靜态html
二、Action Cache
Action Cache相關的helper方法是caches_action和expire_action,其他基本和Page Cache一樣
另外我們還可以運作rake tmp:cache:clear來清空所有的Action Cache和Fragment Cache
- before_filter :authentication
- caches_action :list, :show
如上代碼所示,我們将authentication這個filter放在caches_action之前聲明,這樣我們的Action Cache在執行之前會先通路authentication方法
這樣可以彌補Page Cache不能對需要登入認證的Page進行Cache的缺點
生成的cache檔案為tmp/cache/localhost:3000/blog/list.cache,這樣對不同subdomain的通路頁面可以cache到不同的目錄
由于每次通路Action Cache時都需要與Rails server打交道,并且要先運作filters,是以比Page Cache的效率稍低
三、Fragment Cache
Fragment Cache用于處理rhtml頁面中的部分需要cache的子產品,如app/views/blog/list.rhtml:
- <strong>My Blog Posts</strong>
- <% cache do %>
- <ul>
- <% for post in @posts %>
- <li><%= link_to post.title, :controller => 'blog', :action => 'show', :id => post %></li>
- <% end %>
- </ul>
- <% end %>
生成的cache檔案為/tmp/cache/localhost:3000/blog/list.cache
我們需要在BlogController的list方法裡加上一行判斷,如果是讀取Fragment Cache,則不必再查詢一次資料庫:
- def list
- unless read_fragment({})
- @post = Post.find(:all, \:order => 'created_on desc', :limit => 10)
Fragment分頁時的Cache:
- unless read_fragment({:page => params[:page] || 1}) # Add the page param to the cache naming
- @post_pages, @post = paginate :posts, :per_page => 10
rhtml頁面也需要改寫:
- <% cache ({:page => params[:page] || 1}) do %>
- ... All of the html to display the posts ...
生成的cahce檔案為/tmp/cache/localhost:3000/blog/list.page=1.cache
從分頁的Fragment Cache可以看出,Fragment Cache可以添加類似名字空間的東西,用于區分同一rhtml頁面的不同Fragment Cache,如:
- cache ("turkey") => "/tmp/cache/turkey.cache"
- cache (:controller => 'blog', :action => 'show', :id => 1) => "/tmp/cache/localhost:3000/blog/show/1.cache"
- cache ("blog/recent_posts") => "/tmp/cache/blog/recent_posts.cache"
- cache ("#{request.host_with_port}/blog/recent_posts") => "/tmp/cache/localhost:3000/blog/recent_posts.cache"
清除Fragment Cache的例子:
- expire_fragment(:controller => 'blog', :action => 'list', :page => 1)
- expire_fragment(%r{blog/list.*})
四、ActiveRecord Cache
Rails Edge中ActiveRecord已經預設使用SQl Query Cache,對于同一action裡面同一sql語句的資料庫操作會使用cache
五、memcached
越來越多的大型站點使用memcached做緩存來加快通路速度
memcached是一個輕量的伺服器程序,通過配置設定指定數量的記憶體來作為對象快速通路的cache
memcached就是一個巨大的Hash表,我們可以存取和删除key和value:
- @tags = Tag.find :all
- Cache.put 'all_your_tags', @tags
- Cache.put 'favorite_skateboarder', 'Tom Penny'
- skateboarder = Cache.get 'favorite_skateboarder'
- Cache.delete 'all_your_tags'
我們可以使用memcached做如下事情:
1,自動緩存資料庫的一行作為一個ActiveRecord對象
2,緩存render_to_string的結果
3,手動存儲複雜的資料庫查詢作為緩存
cached_model讓我們輕松的緩存ActiveRecord對象,memcahed-client包含在cached_model的安裝裡,memcahed-client提供了緩存的delete方法
- sudo gem install cached_model
我們在config/environment.rb裡添加如下代碼來使用memcached:
- require 'cached_model'
- memcache_options = {
- :c_threshold => 10_000,
- :compression => true,
- :debug => false,
- :namespace => 'my_rails_app',
- :readonly => false,
- :urlencode => false
- }
- CACHE = MemCache.new memcache_options
- CACHE.servers = 'localhost:11211'
對于production環境我們把上面的代碼挪到config/environments/production.rb裡即可
然後讓我們的domain model內建CachedModel而不是ActiveRecord::Base
- class Foo < CachedModel
然後我們就可以使用memcached了:
- all_foo = Foo.find :all
- Cache.put 'Foo:all', all_foo
- Cache.get 'Foo:all'
需要注意的幾點:
1,如果你查詢的item不在緩存裡,Cache.get将傳回nil,可以利用這點來判斷是否需要重新擷取資料并重新插入到緩存
2,CachedModel的記錄預設15分鐘後expire,可以通過設定CachedModel.ttl來修改expiration time
3,手動插入的對象也會過期,Cache.put('foo', 'bar', 60)将在60秒後過期
4,如果配置設定的記憶體用盡,則older items将被删除
5,可以對每個app server啟動一個memcached執行個體,配置設定128MB的記憶體對一般的程式來說足夠