Rails俄罗斯套娃缓存和N+1问题

6
据我了解,Rails中的俄罗斯套娃缓存(Russian Doll Caching)不利于在进行RDC时急切地加载相关对象或对象列表,因为在RDC中,我们只从数据库中加载顶层对象,查找其缓存的渲染模板并提供服务。如果我们急切地加载相关对象列表,那么如果缓存未过期,这将是无用的。
我的理解正确吗?如果是,我们如何确保在第一次调用时急切地加载所有相关对象,以避免在第一次加载时支付N+1查询的成本(当缓存未启动时)。
1个回答

4

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

Rails缓存指南确实有一个很好的例子,但它被分成了几个部分。查看缓存集合的常见用例(即Rails中的index操作):

<% 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 %>

这个(简化的)例子只进行了一个简单的数据库调用 Product.maximum(:updated_at),以避免执行更加昂贵的调用 Product.all
对于一个冷缓存(第二个问题),通过急加载关联对象来避免 N+1 是很重要的。然而,我们知道我们需要执行这个昂贵的调用,因为集合的第一次缓存读取失败了。在 Rails 中,通常使用 includes 来实现这一点。如果一个 Product 属于多个 Order,那么可以这样做:
<% 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”。
更新:这篇博客文章描述了一种更复杂的模式,以进一步优化部分缓存集合的响应构建。它不是重建整个集合,而是批量获取所有可用的缓存值,然后查询剩余值(并更新缓存)。这有几个好处:批量缓存读取比N+1缓存读取更快,构建缓存的数据库批量查询也更小。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接