如何优化ruby on rails统计每月查询

时间:2012-06-25 18:24:14

标签: sql ruby-on-rails ruby database ruby-on-rails-3

我开发了一个应用程序,为登录用户提供每月统计信息的概述。

这是我目前的做法:

Statistics.html.haml:

#(@parsed months is an array of monthnames.)

- @parsed_months.each do |month|
  = render :partial => "statistic", :locals => {:month => month}

_statistic.html.haml:

%tr{:class => cycle("odd", "even")}
  %td= l(month, :format => "%B").capitalize
  %td= current_user.total_views_count(month)
  %td= current_user.total_leads_count(month)
  %td= current_user.total_clicks_count(month)

返回总视图的方法(在User.rb中):

def total_views_count(month = nil)
  if month == nil
    v = 0
    self.companies.each {|c| v += c.counts.size}
    return v
  else
    v = 0
    self.companies.each {|c| v += c.counts.where(:created_at => Date.today.beginning_of_year..Date.today.end_of_year).where(:created_at => month.beginning_of_month..month.end_of_month).size}
    return v
  end
end

Company.rb:

belongs_to :user
has_many :counts, :as => :countable, :dependent => :destroy

Count.rb:

belongs_to :countable, :polymorphic => true

User.rb:

has_many :companies

这表现不错,但几个月后,Count模型已经增长到一百万条+记录,导致heroku请求超时。

我可以做些什么来优化此查询,或者有更好的方法来执行此操作?

提前致谢!

2 个答案:

答案 0 :(得分:1)

您应该注意以下优化查询:

  1. 尝试在单个请求中减少查询
  2. 优化索引
  3. 创建汇总表
  4. 第2点和第3点与@opensourcechris提到的相同。

    我有一段时间没有使用活动记录,所以我不能给你查询的arel语法,但主要问题是因为有很多数据,你在一个请求中做了很多繁重的查询。您应该使用连接来减少查询并仔细使用索引来使连接和查询最佳。使用连接查询将如下所示:

    SELECT count(c.id) FROM users u
      JOIN companies comp ON comp.user_id = u.id
      JOIN counts c ON c.company_id = comp.id
                       AND c.countable_type = 'Company'
                       AND c.created_at BETWEEN date_range
      WHERE u.id = currrent_user_id
    

    您也可以在此处使用GROUP BY在一个查询中检索所有月份的数据,并按月保留计数。

    要使联接有效工作,您应该在companies.user_id上建立索引,在counts.countable_id, counts.countable_type, counts.created_at上设置复合索引。

    现在应该这样做,但是由于数量已经增加到数百万加上数字,这在短期内无法解决问题。随着计数表的增长,即使这个查询也会开始变慢。在关系数据库中,查询时间随着行数的增加几乎呈线性增加,但在某个阈值之后,它开始以更快的速度增长。因此,通常需要包含您需要使用的表的大小。那是滚动表进入画面的时候。

    随着这一数量的数据进入插入速度也是一个问题。因此,您应该创建一个没有任何索引的表,并记录该表中的所有计数数据。数据可以定期汇总到其他表中。可以根据报告的粒度创建汇总表。常见选项是每小时,每日,每周,每月和每年汇总表。

    还可以将数据转储到存档表中以保留历史记录,以便可以使用不同的粒度或其他要求随时重新创建汇总表。将数据转储到存档表中后,可以从主表中清除它,以便插入速度不会随着时间的推移而受到影响。它还允许记录任何视图,而不必担心像10分钟规则这样的约束,因为数据可以在滚动之前清理。

    PS:我猜你应该使用session_id(uuid)和ip地址来正确计算视图。通常,许多互联网用户共享公共IP地址。

答案 1 :(得分:0)

要优化查询,您应该首先审核每个表上的索引。由于您的WHERE位于日期字段上,我认为索引可以很好地使用它来查看索引:

USE *database*;
SHOW INDEX FROM *tablename*;

然后确保您索引列的位置。

另一种选择是忘记计算历史月份的总数,只需在月末计算它们并将它们存储在新的汇总表中。因此,您将动态计算当前月份的数据,可以从新的汇总表中返回前几个月。