2016-01-06 55 views
5

我有一些代碼正在生成N + 1數據庫查詢問題。Rails 4:未緩存頁面的小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)幫助程序方法在沒有任何預先加載的情況下連續調用用戶模型。但是,由於我緩存了我的觀點,所以這種低效率只發生一次。如果我改變我的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

任何想法如何我可以解決這個小毛刺?

謝謝!

+0

這並不真正配合意義。 'includes'應該使用一個查詢來加載用戶。其他事情可能正在發生。你在開發中啓用緩存來測試它嗎? – Mohamad

+0

我覺得這是'article.username'調用,通過一個額外的請求在模型中查找它。 – jbehrens94

+0

@Mohamad我剛開發時禁用了緩存。我現在一直從Bullet收到一條消息,說 '''N + 1查詢檢測到 Article => [:user] 加入您的查找器::includes => [:user] N + 1查詢方法調用堆棧 〜 /myapp/app/models/article.rb:64:in'username'''' – DaniG2k

回答

0

不知怎的,這解決了我的問題:

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

所以我乾脆就委託了一篇文章給用戶模式的用戶名呼叫。美麗,乾淨,並做到這一點:子彈不再抱怨。

0

我剛來到一個瘋狂的解決方案:你可以檢查控制器中是否存在緩存片段。但問題是Rails會自動將文件摘要添加到緩存鍵。所以,我的「解決方案」:改變緩存的方式來水木清華這樣

# 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來採取特別措施。

+0

謝謝你。實際上,我認爲問題可能出在Bullet上,而不是用'.includes(:user)'。我會報告並看看他們的想法。 – DaniG2k

+0

至於我,'bullet'在這種情況下是正確的,因爲當頁面被緩存時'includes'確實是不必要的。但自從你瞭解它並且它是理想的行爲以後,你可以關閉警告。 – hedgesky