Rails 4:未缓存页面的小N + 1问题

时间:2016-01-06 11:47:04

标签: ruby-on-rails

我有一些代码正在生成N + 1数据库查询问题。

只有在未缓存页面时才会出现此问题。页面缓存后,添加.includes实际上会导致不必要的数据库调用。我想知道如何解决这个问题。

我的 applicaiton_helper.rb 包含以下内容:

module ApplicationHelper
  def by(article)
    "By #{article.username} on #{article.created_at.strftime('%B %e, %Y')}"
  end
end

我的 article.rb 包含:

class Article < ActiveRecord::Base
  belongs_to :user

  def username
    user.username
  end
end

我的 articles_controller.rb 包含:

class ArticlesController < ApplicationController
  def index
    @articles = user_signed_in? ? Article.all : Article.all.published.limit(13)
  end
end

有问题的方法是username方法,它调用User模型。如前所述,当页面尚未被缓存时,这会导致by(article)辅助方法连续调用User模型而不需要任何预先加载。但是,由于我正在缓存我的观点,这种低效只会发生一次。如果我将articles_controller.rb更改为以下内容:

class ArticlesController < ApplicationController
  def index
    @articles = user_signed_in? ? Article.all.includes(:user) : Article.all.published.limit(13).includes(:user)
  end
end

N + 1问题在第一页加载时消失,但是在重新加载页面时我得到了一个不必要的.includes

知道如何修复这个小故障吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

我刚刚找到了一个疯狂的解决方案:您可以检查控制器中是否存在缓存片段。但问题是Rails会自动将文件摘要添加到缓存密钥。所以,我的解决方案&#34;:改变缓存方式,就像这样

# in your view:
<% cache 'foo', skip_digest: true do %>
  contents
<% end %>

然后在控制器中你可以检查片段是否已被缓存:

def index
  if fragment_exist?('asd')
    @articles = user_signed_in? ? Article.all : Article.published.limit(13)
  else
    @articles = user_signed_in? ? Article.all.includes(:user) : Article.published.limit(13).includes(:user)
  end
end

显然,关闭摘要并不是一个很好的解决方案:在解决当前问题时,它会增加一个新问题。但它有效:)

我无法找到一种在控制器中获取视图摘要的方法,但无论如何我认为对于像一次性N + 1这样的小问题来说会有点过分。如果困扰你的只是bullet警告,你可以turn them off采取特定行动。

答案 1 :(得分:0)

不知怎的,这解决了我的问题:

class Article < ActiveRecord::Base
  belongs_to :user
  delegate :username, to: :user
end

所以我只是将文章的用户名调用委托给User模型。美丽,干净,并且诀窍:子弹不再抱怨。