【问题标题】:Rails 3 and memcaching an associationRails 3 和缓存关联
【发布时间】:2012-11-09 14:10:14
【问题描述】:

Following.rb

belongs_to :show

def cached_show
  Rails.cache.fetch([self, :show]) do
    show      
  end
end

查看:

<% @recently_favorited.each do |following| %>
<li>
  <%= link_to "#{following.cached_show.name}", show_path(:permalink => following.cached_show.permalink) %> <span><%= "(#{pluralize(following.cached_show.followers, "follower")})" %></span>
</li>
<% end %>

控制台中的结果:

Cache read: followings/632770-20120929132253/show
Cache generate: followings/632770-20120929132253/show
  Show Load (0.7ms) SELECT `shows`.* FROM `shows`WHERE `shows`.`id` = 617 LIMIT 1
Cache write: followings/632770-20120929132253/show

Cache read: followings/632770-20120929132253/show
Cache fetch_hit: followings/632770-20120929132253/show

Cache read: followings/632770-20120929132253/show
Cache fetch_hit: followings/632770-20120929132253/show

问题:
这甚至是获取/缓存关联的“正确”实现吗?
那么性能呢?
在某些视图中(如示例中),它会在每个循环中命中缓存 3 次。在我的情况下,我在页脚中循环了 10 个项目,因此每个请求都会命中 30 次。这样可以吗,还是每个循环一个 n+1 查询会更好?

赞赏建议和一般最佳实践:)

【问题讨论】:

    标签: ruby-on-rails-3 memcached associations


    【解决方案1】:

    据我所知,创建一个独特的方法来访问缓存与获取缓存并不常见。

    大多数时候,您只需调用一个一直询问cache 的方法,因为如果您在缓存键中包含一个对象,updated_at 字段将用于构建键。

    对于您现在的示例,奇怪的部分是您实际上并没有对 Following 模型做任何事情,除了访问它的关联。因此,您应该直接在Show 模型上查询:

    @recently_favorited_shows = Show.joins(:followings).order("followings.created_at DESC").uniq
    

    然后在您看来,循环播放节目。只有一个查询,没有n+1

    如果您预计会有数千次点击,我只是建议缓存 @recently_favorited_shows 的结果并每 X 分钟过期一次:

    @recently_favorited_shows = cache_store.fetch('recently_favorited_shows', expires_in: 5.minutes){Show.joins(:followings).order("followings.created_at DESC").uniq}
    

    另一方面,如果您想在某个时候这样做,这里有一篇关于视图端缓存使用情况的好文章:http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works

    无连接解决方​​案

    编辑:现在,如果您在 followings 表中有数百万行,我会这样做:

    • shows 表上创建一个字段last_followed_at,上面有一个索引
    • Following.rbbelongs_to :show, touch: :last_followed_at。这样,只要您在 Following 中添加新条目,它就会更新 shows 表中的字段
    • 然后,要获取最新关注的节目,请执行以下操作:

      @shows = Show.order("last_followed_at DESC").limit(10) # Fast query of course
      

    【讨论】:

    • 在我的开发环境中,您的查询甚至没有在 1 分钟内完成。我可能会丢失一个索引,我不确定,但我认为加入这么多(比如 1M 行)会很慢。我将缓存两个查询的结果。
    • 考虑为请求添加索引和限制,因为您不需要所有节目,但需要最新的 n 节目
    • 不,我需要最新的n 以下内容:) 抱歉没有发布该代码。
    • 编辑了我的答案,因此您无需进行任何连接,因为您将拥有一个大数据集
    • 啊,很好的解决方案! :) 谢谢
    【解决方案2】:

    这不能回答我的问题,但它解决了我的问题。下面是我的做法:

    @shows = Rails.cache.fetch("recently_favorited_shows", expires_in: 1.minutes) do
      Show.find(Following.order("created_at DESC").limit(10).collect(&:show_id))
    end
    

    查询非常快(约 0.8 毫秒,每个显示 IRB 控制台)

    【讨论】:

    • 如果您替换最后一个方法调用,您可以使查询更快一点。尝试使用.limit(10).pluck(:show_id),而不是.limit(10).collect(&amp;:show_id)。直接返回一个 show ids 数组,而不首先实例化以下对象,稍后它必须进行 GC。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 2011-12-15
    • 2015-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多