Rails Cache 缓存

发布于 2024-04-25 02:41:03 字数 6411 浏览 46 评论 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技术交流群

发布评论

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

关于作者

聊慰

暂无简介

文章
评论
27 人气
更多

推荐作者

浪子阿飞

文章 0 评论 0

JK.Yang

文章 0 评论 0

人间不值得

文章 0 评论 0

静待花开

文章 0 评论 0

只涨不跌

文章 0 评论 0

污浊的双黑

文章 0 评论 0

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