天天看點

Rails緩存

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代碼  

  1. config.action_controller.perform_caching = true

而production環境下預設是開啟cache功能的

Page Cache是Rails中最快的cache機制,使用Page Cache的前提一般為:

1,需要cache的page對所有使用者一緻

2,需要cache的page對public可通路,不需要authentication

Page Cache使用起來很簡單:

  1. class BlogController < ApplicationController  
  2.  caches_page :list, :show  
  3.  def list  
  4.    Post.find(:all, \:order => "created_on desc", :limit => 10)  
  5.  end  
  6.  def show  
  7. @post = Post.find(params[:id])  
  8. 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:

  1. Rails::Initializer.run do |config|  
  2.  # ...  
  3.  config.load_paths += %w(#{RAILS_ROOT}/app/sweepers)  

這告訴Rails加載#{RAILS_ROOT}/app/sweepers目錄下的檔案

我們為BlogController定義app/sweepers/blog_sweeper.rb:

  1. class BlogSweeper < ActionController::Caching::Sweeper  
  2.  observe Post # This sweeper is going to keep an eye on the Post model  
  3.  # If our sweeper detects that a Post was created call this
  4.  def after_create(post)  
  5.    expire_cache_for(post)  
  6.  # If our sweeper detects that a Post was updated call this
  7.  def after_update(post)  
  8.  # If our sweeper detects that a Post was deletedcall this
  9.  def after_destroy(post)  
  10. private
  11.  def expire_cache_for(record)  
  12.    # Expire the list page now that we posted a new blog entry  
  13.    expire_page(:controller => 'blog', :action => 'list')  
  14.    # Also expire the show page, in case we just edit a  blog entry  
  15.    expire_page(:controller => 'blog', :action => 'show', :id => record.id)  

然後我們在BlogController裡加上該sweeper即可:

  1.  cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]  

我們可以配置cache的靜态html檔案的存放位置,這在config/environment.rb裡設定:

  1. 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

  1.  before_filter :authentication  
  2.  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:

  1. <strong>My Blog Posts</strong>  
  2. <% cache do %>  
  3.  <ul>  
  4.    <% for post in @posts %>  
  5.      <li><%= link_to post.title, :controller => 'blog', :action => 'show', :id => post %></li>  
  6.    <% end %>  
  7.  </ul>  
  8. <% end %>  

生成的cache檔案為/tmp/cache/localhost:3000/blog/list.cache

我們需要在BlogController的list方法裡加上一行判斷,如果是讀取Fragment Cache,則不必再查詢一次資料庫:

  1. def list  
  2.  unless read_fragment({})  
  3. @post = Post.find(:all, \:order => 'created_on desc', :limit => 10)  

Fragment分頁時的Cache:

  1.  unless read_fragment({:page => params[:page] || 1}) # Add the page param to the cache naming  
  2. @post_pages, @post = paginate :posts, :per_page => 10

rhtml頁面也需要改寫:

  1. <% cache ({:page => params[:page] || 1}) do %>  
  2.  ... 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,如:

  1. cache ("turkey") => "/tmp/cache/turkey.cache"
  2. cache (:controller => 'blog', :action => 'show', :id => 1) => "/tmp/cache/localhost:3000/blog/show/1.cache"
  3. cache ("blog/recent_posts") => "/tmp/cache/blog/recent_posts.cache"
  4. cache ("#{request.host_with_port}/blog/recent_posts") => "/tmp/cache/localhost:3000/blog/recent_posts.cache"

清除Fragment Cache的例子:

  1. expire_fragment(:controller => 'blog', :action => 'list', :page => 1)  
  2. expire_fragment(%r{blog/list.*})  

四、ActiveRecord Cache

Rails Edge中ActiveRecord已經預設使用SQl Query Cache,對于同一action裡面同一sql語句的資料庫操作會使用cache

五、memcached

越來越多的大型站點使用memcached做緩存來加快通路速度

memcached是一個輕量的伺服器程序,通過配置設定指定數量的記憶體來作為對象快速通路的cache

memcached就是一個巨大的Hash表,我們可以存取和删除key和value:

  1. @tags = Tag.find :all  
  2. Cache.put 'all_your_tags', @tags
  3. Cache.put 'favorite_skateboarder', 'Tom Penny'
  4. skateboarder = Cache.get 'favorite_skateboarder'
  5. Cache.delete 'all_your_tags'

我們可以使用memcached做如下事情:

1,自動緩存資料庫的一行作為一個ActiveRecord對象

2,緩存render_to_string的結果

3,手動存儲複雜的資料庫查詢作為緩存

cached_model讓我們輕松的緩存ActiveRecord對象,memcahed-client包含在cached_model的安裝裡,memcahed-client提供了緩存的delete方法

  1. sudo gem install cached_model  

我們在config/environment.rb裡添加如下代碼來使用memcached:

  1. require 'cached_model'
  2. memcache_options = {  
  3.  :c_threshold => 10_000,  
  4.  :compression => true,  
  5.  :debug => false,  
  6.  :namespace => 'my_rails_app',  
  7.  :readonly => false,  
  8.  :urlencode => false
  9. }  
  10. CACHE = MemCache.new memcache_options  
  11. CACHE.servers = 'localhost:11211'

對于production環境我們把上面的代碼挪到config/environments/production.rb裡即可

然後讓我們的domain model內建CachedModel而不是ActiveRecord::Base

  1. class Foo < CachedModel  

然後我們就可以使用memcached了:

  1. all_foo = Foo.find :all  
  2. Cache.put 'Foo:all', all_foo  
  3. 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的記憶體對一般的程式來說足夠

繼續閱讀