Rails Русская кукла Кэширование и N+1
Из того, что я понимаю о кэшировании русской куклы в Rails, было бы вредно загружать связанные объекты или списки объектов, когда мы выполняем RDC (кэширование русской куклы), потому что в RDC мы просто загружаем объект верхнего уровня из базы данных и ищем его кешируется, выдается шаблон и подается. Если бы мы стремились загрузить списки связанных объектов, это было бы бесполезно, если кеш не устарел.
Правильно ли мое понимание? Если да, то как нам убедиться, что мы стремимся загрузить все связанные объекты при первом вызове, чтобы не оплачивать N+1 запросов во время самой первой загрузки (когда кэш не грел)?
1 ответ
Правильно - при загрузке коллекции или сложного объекта со многими ассоциациями можно избежать дорогостоящего вызова быстрой загрузки всех объектов и ассоциаций, выполнив быстрый и простой вызов.
Руководство по рельсу для кеширования имеет хороший пример, но оно немного разбито. Рассматривая общий случай использования кэширования коллекции (например, действие index в 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
с, то что-то вроде:
<% 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
s ассоциации правильно настроены на touch
когда связанные объекты обновляются.
Обновление: этот пост в блоге описывает более сложный шаблон для дальнейшей оптимизации построения ответов для частично кэшированных коллекций. Вместо того, чтобы перестраивать всю коллекцию, он массово выбирает все доступные кэшированные значения, а затем выполняет массовый запрос оставшихся значений (и обновляет кэш). Это полезно несколькими способами: массовое чтение из кэша выполняется быстрее, чем чтение из N+1 кэша, а объемный запрос к БД для создания кэша также меньше.