繁体   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