我有一个主页,它在整个页面上呈现了一些部分。它也有会话标题(登录)。部分包含分页的书籍集。现在我想缓存这个部分,因为它每周更新一次。
- 问题 1:如何缓存特定的部分(不打 db)?
- 问题 2:当我更新图书模型时,如何删除(过期)缓存内容?
我有一个主页,它在整个页面上呈现了一些部分。它也有会话标题(登录)。部分包含分页的书籍集。现在我想缓存这个部分,因为它每周更新一次。
您在这里寻找片段缓存,它发生在视图层上。片段缓存和存储内容的过期非常容易做到。你有一个书籍清单,所以假设你的视图看起来有点像这样:
<ul>
<% @books.each do |book| %>
<li><%= book.name %></li>
<% end %>
</ul>
要为这一位启用缓存,只需将其包装在cache
:
<% cache do %>
<ul>
<% @books.each do |book| %>
<li><%= book.name %></li>
<% end %>
</ul>
<% end %>
当然,这并没有为缓存命名或对它做任何特别的事情……虽然 Rails 会为这个缓存片段自动选择一个唯一的名称,但它并没有真正的帮助。我们可以做得更好。让我们使用 DHH 的基于键的缓存过期技术,并给缓存一个与其内容相关的名称。
<% cache ['book-list', *@books] do %>
<ul>
<% @books.each do |book| %>
<li><%= book.name %></li>
<% end %>
</ul>
<% end %>
将参数传递到缓存中会根据提供的参数构建缓存键。字符串是直接传入的——所以,在这里,缓存总是以“book-list”开头。这是为了防止与您可能正在缓存相同内容但具有不同视图的其他地方发生缓存冲突。对于@books 数组的每个成员,Rails 将调用cache_key
: 对于 ActiveRecord 对象,这会产生一个由其模型、ID 以及最重要的是对象上次更新时间组成的字符串。
这意味着当你更新对象时,这个片段的缓存键将会改变。换句话说,它会自动过期——当一本书更新时,这个缓存语句将搜索一个不存在的键,断定它不存在,并用新的内容填充它。旧的、陈旧的内容将在您的缓存存储中徘徊,直到被内存或年龄限制驱逐(memcached 会自动执行此操作)。
我在许多生产应用程序中使用了这种技术,并且效果很好。有关更多信息,请查看37signals 帖子,有关 Rails 中的一般缓存信息,请参阅Ruby on Rails 缓存指南。
“计算机科学只有两个难题:缓存失效和命名事物。”
——菲尔·卡尔顿
缓存
Rails 缓存指南可能始终是Rails必须提供的内置缓存策略的一个很好的切入点。无论如何,这是我非常简单的缓存方法
# _partial.html.erb
<% cache(some_key, :expires_in => 1.week) do %>
<%# ... content %>
<% end %>
现在 somesome_key
可以是任何字符串,只要它是唯一的。但话又说回来,让我们尝试更聪明一点,让密钥以某种方式依赖于书籍列表。假设您实际上传入了books
一些返回的查询数组,然后 rails 调用cache_key
它的每个条目并最终为该集合构造一个唯一键。因此,当集合更改时,键会更改。那是因为在所有模型cache_key
中实现ActiveRecord::Base
并因此可用。而且,如果可用,它甚至会使用时间戳。
但话又说回来,每次发出请求时,这都会击中数据库。代码相同:
# controller_method
@weekly_books = Books.where 'condition'
# _partial.html.erb
<% cache(@weekly_books) do %>
<%# ... content %>
<% end %>
为了避免经常访问数据库,您还可以通过包装调用来缓存查询本身:
# Book.rb
def self.weeklies
Rails.cache.fetch("book_weeklies", :expires_in => 1.day) do
Books.where 'condition'
end
end
# controller_method
@weekly_books = Books.weeklies
# _partial.html.erb
<% cache(@weekly_books) do %>
<%# ... content %>
<% end %>