假设您有一个显示最新帖子的页面片段,您将在30分钟后过期。我在这里使用Rails。
<% cache("recent_posts", :expires_in => 30.minutes) do %>
...
<% end %>
显然,如果片段存在,您不需要进行数据库查找以获取最新的帖子,因此您也应该能够避免这种开销。
我现在正在做的是在控制器中这样的东西似乎有效:
unless Rails.cache.exist? "views/recent_posts"
@posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end
这是最好的方法吗?这样安全吗?
我不明白的一件事是,为什么密钥为“recent_posts
”,片段为“views/recent_posts
”,以后查看,但在看完memcached -vv
之后我想出了这个看看它的用途。此外,我不喜欢手动输入“recent_posts
”的重复,最好将其保存在一个地方。
想法?
答案 0 :(得分:12)
Evan Weaver的Interlock Plugin解决了这个问题。
如果您需要不同的行为,例如更细粒度的控制,您也可以轻松地自己实现这样的功能。基本思想是将控制器代码包装在一个块中,该块仅在视图需要该数据时才实际执行:
# in FooController#show
@foo_finder = lambda{ Foo.find_slow_stuff }
# in foo/show.html.erb
cache 'foo_slow_stuff' do
@foo_finder.call.each do
...
end
end
如果您熟悉ruby元编程的基础知识,那么很容易将其打包成更符合您口味的API。
这比将finder代码直接放在视图中要好:
我认为cache_fu可能在其中一个版本/分叉中具有类似功能,但无法具体回忆。
从memcached获得的优势与缓存命中率直接相关。注意不要浪费您的缓存容量,并通过多次缓存相同的内容而导致不必要的错过。例如,不要同时缓存一组记录对象及其html片段。通常,片段缓存将提供最佳性能,但它实际上取决于应用程序的具体情况。
答案 1 :(得分:3)
如果缓存在您在控制器中检查它之间到期,会发生什么情况 以及在视图渲染中检查它的时间?
我在模型中创建了一个新方法:
class Post
def self.recent(count)
find(:all, :limit=> count, :order=>"updated_at DESC")
end
end
然后在视图中使用它:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
<% Post.recent(20).each do |post| %>
...
<% end %>
<% end %>
为清楚起见,您还可以考虑将最近帖子的呈现移动到其自己的部分中:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
<%= render :partial => "recent_post", :collection => Post.recent(20) %>
<% end %>
答案 2 :(得分:1)
您可能还想查看
允许您这样做:
<% cache("recent_posts", :expires_in => 30.minutes) do %>
...
<% end %>
控制器
unless fragment_exist?("recent_posts")
@posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end
虽然我承认DRY的问题仍然需要两个地方的密钥名称。我通常这样做与Lars的建议类似,但这取决于品味。我知道的其他开发人员坚持使用检查片段。
更新
如果您查看片段文档,您可以看到它如何摆脱需要视图前缀:
# File vendor/rails/actionpack/lib/action_controller/caching/fragments.rb, line 33
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
答案 3 :(得分:1)
Lars非常关注使用以下几点失败的可能性:
unless fragment_exist?("recent_posts")
因为检查缓存和使用缓存之间存在差距。
jason提到的插件(Interlock)通过假设如果你正在检查片段是否存在而非常优雅地处理它,那么你可能也会使用片段,从而在本地缓存内容。我出于这些原因使用Interlock。
答案 4 :(得分:1)
就像一个想法:
在应用程序控制器中定义
def when_fragment_expired( name, time_options = nil )
# idea of avoiding race conditions
# downside: needs 2 cache lookups
# in view we actually cache indefinetely
# but we expire with a 2nd fragment in the controller which is expired time based
return if ActionController::Base.cache_store.exist?( 'fragments/' + name ) && ActionController::Base.cache_store.exist?( fragment_cache_key( name ) )
# the time_fraqgment_cache uses different time options
time_options = time_options - Time.now if time_options.is_a?( Time )
# set an artificial fragment which expires after given time
ActionController::Base.cache_store.write("fragments/" + name, 1, :expires_in => time_options )
ActionController::Base.cache_store.delete( "views/"+name )
yield
end
然后在任何动作中使用
def index
when_fragment_expired "cache_key", 5.minutes
@object = YourObject.expensive_operations
end
end
在视图中
cache "cache_key" do
view_code
end