Rails Cache 缓存

发布于 2024-04-25 02:41:03 字数 6411 浏览 33 评论 0

这周修改 bug 的时候有一个是由于缓存机制引起的,遂系统的学习了下 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

Page Cache

如果开发阶段要使用 cache,则需要先设置好 config/environments/development.rb:

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  
end

这样我们就对 BlogController 的 list 和 show 页面进行了缓存,这样做的效果是第一次访问 list 和 show 页面时生成了 public/blog/list.htmlpublic/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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

聊慰

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

内心激荡

文章 0 评论 0

JSmiles

文章 0 评论 0

左秋

文章 0 评论 0

迪街小绵羊

文章 0 评论 0

瞳孔里扚悲伤

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文