13

在我看来,片段缓存和急切加载——至少有时——有些矛盾。假设我有一个用户,他有很多帖子,每个帖子都有很多评论,而这些评论又可以有很多评论等等。

当我必须渲染页面时,我可以选择预先加载用户、她的所有帖子、他们的所有评论等,以避免访问数据库 n-1 次。或者我可以懒惰地加载每个对象并依靠片段缓存来仅查询数据库中的新对象或已更改的对象。同时使用片段缓存和急切加载似乎很浪费,因为我可能会执行非常复杂的查询并实例化大量对象而只使用其中的一小部分。

但是,如果我有一个应用程序,其中用户有许多 Foo,而这些 Foo 又具有许多 Bars 等等,但其中每个 Foo 同时创建完整的所有 Bars 及其关联对象,从那时起永远不会改变. 在这种情况下,我想对已经渲染的 Foo 使用片段缓存,但是当我必须加载一个新的 Foo 及其所有关联对象时使用急切加载。毕竟,在更细粒度的级别缓存片段并没有什么好处。

Rails 中实现此目的的最佳方法是什么?我想我可以做一个查询来只获取 Foo 的 id,然后在我必须渲染每个 Foo 时使用急切加载进行显式查找。有没有更好/更优雅/更惯用的方式来做到这一点?

4

4 回答 4

0

您可以使用fragment_exists?控制器中的方法来防止在所有对象已经在缓存中时立即加载它们。仅在第一次调用页面时不会出现这种情况。

像这样:

  if fragment_exists? "my_cache_key_#{id}" 
    # load your object without eager loading here        
  else
    # eager load your objects here        
  end

然后在视图中使用片段 chaching:

<% cache("my_cache_key_#{@object.id}") do %>
...
...
...
<% end %>

那应该为你做!

于 2014-05-20T14:44:47.597 回答
0

我对使用 lambda 的缓存做了类似的事情。下面你可以得到一个想法。它解决的问题 - 获得最受欢迎的用户是一项繁重的操作,需要 > 5 秒。但是使用 lambda,您可以缓存用户列表。

controller:
def index
 @users = -> { User.by_rating }
end

view:
= cache "rating-list", expires_in: 1.day do
  - @users.call.each do |user|
     = render user
于 2018-02-20T14:58:42.643 回答
0

基于@daviddb 对 Rails 4 的回答,我发现有必要skip_digest在视图中为控制器中的模板重新创建 MD5 哈希以进行比较似乎有点多。

此外,如果没有第一次找到对象,将很难获得具有最后修改时间戳的对象,所以我发现在没有的情况下进行初始查询很有帮助.includes(:object1, :object2)

views/customers/show.html.slim(调整您喜欢的模板引擎)

- cache['customers/show', @customer], skip_digest: true do
  h1
    = @customer.account.name
  = render 'account_summary', account: @customer.account
  = render 'account_details', transactions: @customer.account.transactions
  ...

controllers/customers_controller.rb

  def show
    customer = Customer.find(params[:id])
    if fragment_exist?(['customers/show', customer])
      @customer = customer
    else
      @customer = Customer.includes(account: :transactions).find(params[:id])
    end
  end

注意:通过设置skip_digest: true,您需要在更改此视图及其依赖的任何部分时在部署时清除缓存,以确保正确呈现新布局。

于 2015-11-17T03:18:34.593 回答
0

您可以使用俄罗斯娃娃缓存来解决此类问题,例如我们有包含评论和作者的帖子,在主页中您可以使用

// HomeController

def index
  @posts = Post.includes(:author, :comments).limit(10)
end

看法

- cache ["v1#home", @posts.maximum(:updated_at).to_i] do
  - @post.each do |post|
    - cache ["v1#post_in_home", @post.id, @post.updated_at.to_i] do
      = post.title
      = post.author.name
      = post.content

      - cache ["v1#comments_in_post", @post.comments.maximum(:updated_at).to_i] do
        - @comments.each do |comment|
          = comment.author
          = comment.content
于 2020-06-23T15:27:26.230 回答