【问题标题】:Rails Russian Doll Caching and N+1Rails 俄罗斯娃娃缓存和 N+1
【发布时间】:2014-03-19 00:13:10
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

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

    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 个简单的 DB 调用 Product.maximum(:updated_at),以避免进行更昂贵的调用 Product.all

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多