Rails Cache 缓存
这周修改 bug 的时候有一个是由于缓存机制引起的,遂系统的学习了下 rails 的缓存,在这里记录下。
Rails 的 Cache 分四种:
- Page Cache - Fastest
- Action Cache - Next Fastest
- Fragment Cache - Least Fastest
- ActiveRecord Cache - Only available in Edge Rails
Page Cache
如果开发阶段要使用 cache,则需要先设置好 config/environments/development.rb:
config.action_controller.perform_caching = true
而 production 环境下默认是开启 cache 功能的
Page Cache 是 Rails 中最快的 cache 机制,使用 Page Cache 的前提一般为:
- 需要 cache 的 page 对所有用户一致
- 需要 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
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)
end
# If our sweeper detects that a Post was updated call this
def after_update(post)
expire_cache_for(post)
end
# If our sweeper detects that a Post was deletedcall this
def after_destroy(post)
expire_cache_for(post)
end
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)
end
end
然后我们在 BlogController 里加上该 sweeper 即可:
class BlogController < ApplicationController
caches_page :list, :show
cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]
# ...
end
我们可以配置 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
class BlogController < ApplicationController
before_filter :authentication
caches_action :list, :show
cache_sweeper :blog_sweeper, \:only => [:create, :update, :destroy]
如上代码所示,我们将 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)
end
end
Fragment 分页时的 Cache:
def list
unless read_fragment({:page => params[:page] || 1}) # Add the page param to the cache naming
@post_pages, @post = paginate :posts, :per_page => 10
end
end
rhtml 页面也需要改写:
<% cache ({:page => params[:page] || 1}) do %>
... All of the html to display the posts ...
<% end %>
生成的 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Java 异常小总结
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论