Rails俄罗斯娃娃缓存和N + 1

时间:2014-02-18 23:00:23

标签: ruby-on-rails ruby-on-rails-3 caching russian-doll-caching cache-digests

根据我对Rails中俄罗斯玩偶缓存的理解,当我们进行RDC(俄罗斯玩偶缓存)时,这对于急切的加载相关对象或对象列表是有害的,因为在RDC中我们只是从数据库加载顶级对象,并且查找其缓存的渲染模板并提供服务。如果我们要急切加载相关的对象列表,如果缓存不是陈旧的话,这将是无用的。

我的理解是否正确?如果是,我们如何确保在第一次调用时急切加载所有相关对象,以免在第一次加载期间(当缓存不热时)支付N + 1个查询的成本。

1 个答案:

答案 0 :(得分:4)

正确 - 当加载具有许多关联的集合或复杂对象时,通过快速,简单的调用可以避免对急切加载所有对象和关联的代价高昂的调用。

rails guide for caching确实有一个很好的例子,然而,它有点分裂。查看缓存集合的常见用例(即Rails中的索引操作):

<% cache("products/all-#{Product.maximum(:updated_at).try(:to_i)}") do %>
  All available products:
  <% Product.all.each do |p| %>
    <% cache(p) do %>
      <%= link_to p.name, product_url(p) %>
    <% end %>
  <% end %>
<% end %>

此(精简)示例执行1个简单的数据库调用Product.maximum(:updated_at),以避免执行更昂贵的调用Product.all

对于冷缓存(第二个问题),通过急切加载关联对象来避免N + 1是很重要的。但是,我们知道我们需要执行这个昂贵的调用,因为错过了第一个缓存读取集合。在Rails中,这通常使用includes完成。如果Product属于许多Order s,那么类似于:

<% cache("products/all-#{Product.maximum(:updated_at).try(:to_i)}") do %>
  All available products:
  <% Product.includes(:orders).all.each do |p| %>
    <% cache(p) do %>
      <%= link_to p.name, product_url(p) %>
      Bought at:
      <ul>
        <% p.orders.each do |o| %>
          <li><%= o.created_at.to_s %></li>
        <% end %>
      </ul>
    <% end %>
  <% end %>
<% end %>

在冷缓存的情况下,我们仍然为集合和每个成员执行缓存读取,但是,在部分热缓存的情况下,我们将跳过对部分成员的渲染。请注意,当关联对象更新时,此策略依赖于Product个关联正确设置为touch

更新This blog post描述了一种更复杂的模式,可进一步优化部分缓存集合的构建响应。它不是重建整个集合,而是批量获取所有可用的缓存值,然后对剩余值进行批量查询(并更新缓存)。这在以下几个方面很有用:批量缓存读取比N + 1缓存读取更快,并且对DB构建缓存的批量查询也更小。