簡體   English   中英

如何在Rails應用程序中加快查詢速度?

[英]How can I speed up this query in a Rails app?

我需要幫助優化Rails 5 app的一系列查詢。 以下內容說明了我在做什么,但是如果不清楚,請告訴我,我將嘗試進一步詳細介紹。

我的模型中有以下方法:

在我的IncomeReport模型中:

class IncomeReport < ApplicationRecord

  def self.net_incomes_2015_totals_collection
    all.map(&:net_incomes_2015).compact
  end

  def net_incomes_2015
    (incomes) - producer.expenses_2015
  end

  def incomes
    total_yield * 1.15
  end

end

Producer模型中,我具有以下內容:

class Producer < ApplicationRecord

  def expenses_2015
    expenses.sum(&:expense_per_ha)
  end

end

Expense模型中,我有:

class Expense < ApplicationRecord

  def expense_per_ha
    total_cost / area
  end

end

在控制器中,我有這個功能(我正在使用一個稱為descriptive_statistics的gem來獲取最小值,最大值,四分位數等,以防您最后想知道那部分)

@income_reports_2015 = IncomeReport.net_incomes_2015_totals_collection.extend(DescriptiveStatistics)

然后我認為

<%= @income_reports_2015.descriptive_statistics[:min] %> 

當數據庫中只有幾個對象時,此代碼有效。 但是,由於現在有成千上萬的查詢,所以要花很多時間才能得出結果。 它花了很長時間才超時!

我該如何優化以獲得最佳效果?

一種方法可能是以不同的方式構建應用程序。 我認為在這種情況下可能會使用面向服務的體系結構

您可能不希望在用戶進入該視圖時進行查詢,而希望使用輔助程序間歇性地查詢,然后寫入CSV。 因此,用戶導航到該視圖,您可以改為從CSV中讀取內容。 這樣運行起來會更快,因為您無需再在其中進行查詢(當用戶導航到此頁面時),而是從之前作為后台進程創建的文件中讀取內容。

顯然,這有其自身的挑戰,但是我過去已經這樣做以解決類似的問題。 我編寫了一個應用程序,每分鍾一次從10個不同的外部API提取數據。 10次​​不同的訪存導致數據庫中有10個對象。 每天DB中10 * 60 * 24 = 14,400條記錄。 當用戶加載需要此數據的頁面時,他們將加載7天的記錄,100,800個數據庫行。 我遇到了同樣的問題,在運行時執行查詢會導致超時,我寫了一個CSV並作為解決方法將其讀取。

IncomeReport的結構是IncomeReport 通過查看代碼的問題在於allnet_incomes_2015_totals_collection all命中數據庫並返回所有記錄,然后將它們映射。 過度殺傷力。 嘗試過濾數據,減少查詢,減少選擇並直接通過ActiveRecord獲得所需的所有信息。 Ruby循環會減慢速度。

因此,在不知道表結構及其數據的情況下,我將執行以下操作:

def self.net_incomes_2015_totals_collection
 where(created_at: 2015_start_of_year..2015_end_of_year).where.not(net_incomes_2015: nil).pluck(:net_incomes_2015)
end

另外,我將確保為created_at和net_incomes_2015提供一個復合索引。

它可能會很慢,但要比現在好。 您應該考慮在午夜在后台聚合數據(resque,sidekiq等)(並將其緩存嗎?)。

希望能幫助到你。

看來您這里有幾個n + 1個查詢。 每個報告在一個單獨的查詢中獲取其生產者。 然后,每個生產者在不同的查詢中獲取其每個費用。

您可以通過拋出preload(:producer)而不是all來避免出現第一個問題。 然而, sum晚節將很難避免,因為sum會自動閃光的查詢。

您可以通過以下方法避免該問題

def self.net_incomes_2015_totals_collection
  joins(producer: :expenses).
    select(:id, 'income_reports.total_yield * 1.15  - SUM(expenses.total_cost/expenses.area) AS net_incomes_2015').
    group(:id).
    map(&:net_incomes_2015).
    compact
end

在一個查詢中獲取所有內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM