我正在通过Memcached在我的Rails项目中实现缓存,特别是尝试缓存边列块(最新的照片、博客等(,目前我让它们每15分钟左右缓存一次。这很有效,但如果我能像添加、更新新内容之类的更新,那会更好。
我在Memcached上看Scaling Rails的一集http://content.newrelic.com/railslab/videos/08-ScalingRails-Memcached-fixed.mp4,在视频的8:27,Gregg Pollack谈到了Memcached中的智能缓存,其中智能密钥(在本例中,updated_at时间戳(用于替换以前缓存的项目,而不必使缓存过期。因此,无论何时更新时间戳,缓存都会在寻找新的时间戳时刷新,我想。
我在这个例子中使用了我的"最近的照片"侧边栏,这就是它的设置方式。。。
_side-column.html.erb:
<div id="photos"">
<p class="header">Photos</p>
<%= render :partial => 'shared/photos', :collection => @recent_photos %>
</div>
_photos.html.erb
<% cache(photos) do %>
<div class="row">
<%= image_tag photos.thumbnail.url(:thumb) %>
<h3><%= link_to photos.title, photos %></h3>
<p><%= photos.photos_count %> Photos</p>
</div>
</div>
<%结束%>
在第一次运行时,Memcached将块缓存为views/photos/1-20110308040600,并将在页面刷新时重新加载缓存的片段,到目前为止效果良好。然后,我在后端的特定行中添加了一张额外的照片并重新加载,但照片数量没有更新。日志显示,它仍在从views/photos/1-20110308040600加载,并且没有获取更新的时间戳。我所做的一切似乎都和视频中所做的一样,我做错了什么?
此外,这个问题还有第二部分。正如您在上面的部分中看到的,为集合调用@recent_photos查询(在我的lib文件夹中的模块之外(。但是,我注意到,即使块被缓存,这个SELECT查询仍然被调用。起初,我试图将整个分部封装在一个块中,使其<%cache(@recent_photos(可以%>,但显然这不起作用——尤其是因为整个集合上没有真正的时间戳,当然只是单个项目。如果结果已经缓存,如何防止进行此查询?
更新关于第二个问题,我发现除非Rails.cache.exist?可能只是我的选择,但棘手的是使用时间戳的通配符性质。。。
更新2完全无视我的第一个问题,我弄清楚了为什么缓存没有刷新。这是因为updated_at字段没有被更新。原因是我添加/删除了一个项,该项是父项中的嵌套资源,我可能需要对此进行"触摸",以便更新父项中更新的updated_at字段。
但我的第二个问题仍然存在。。。即使片段被缓存,主@recent_photos查询仍在被调用。。。有没有办法使用cache.exists?以一个名为/views/photos/1-2011random的缓存为目标?
Rails缓存的一个主要缺陷是无法可靠地分离缓存组件的控制器和视图。我找到的唯一解决方案是将查询直接嵌入到缓存块中,但最好是通过helper方法。
例如,你可能有这样的东西:
class PhotosController < ApplicationController
def index
# ...
@recent_photos = Photos.where(...).all
# ...
end
end
第一直觉是只有在视图需要时才运行该查询,例如测试缓存内容的存在性。不幸的是,在测试缓存内容和实际呈现页面之间的时间间隔内,内容过期的可能性很小,当使用零值@recent_photos
时,这将导致模板呈现错误。
这里有一个更简单的方法:
<%= render :partial => 'shared/photos', :collection => recent_photos %>
不要使用实例变量,而是使用辅助方法。将你的助手方法定义为你将在控制器中加载:
module PhotosHelper
def recent_photos
@recent_photos ||= Photos.where(...).all
end
end
在这种情况下,会保存该值,以便对同一辅助方法的多次调用只触发一次查询。这在您的申请中可能没有必要,可以省略。毕竟,该方法所要做的就是返回一个"最近的照片"列表。
如果Rails支持具有自己关联视图的子控制器,那么这种混乱就可以消除,这是对这里使用的模式的一种变体。
自从提出这个问题以来,我一直在进一步研究缓存,我想我已经开始确切地理解这种缓存技术的价值了。
例如,我有一篇文章,通过页面所需的各种内容,包括查询其他表,我可能需要对每篇文章进行5-7个不同的查询。但是,以这种方式缓存文章会将所有这些查询减少为一个查询。
我假设使用这种技术,总是需要至少有"一个"查询,因为需要有"某种"方法来判断时间戳是否已更新。