繁体   English   中英

优化Rails循环中的内存使用

[英]optimize memory usage in rails loop

我在雪松堆栈上开发了一个Heroku Rails应用程序,这就是瓶颈。

def self.to_csvAlt(options = {})
  CSV.generate(options) do |csv|     
    column_headers = ["user_id", "session_id", "survey_id"]
    pages = PageEvent.order(:page).select(:page).map(&:page).uniq
    page_attributes = ["a", "b", "c", "d", "e"]
    pages.each do |p|
      page_attributes.each do |pa|
        column_headers << p + "_" + pa
      end
    end
    csv << column_headers
    session_ids = PageEvent.order(:session_id).select(:session_id).map(&:session_id).uniq
    session_ids.each do |si|
        session_user = PageEvent.find(:first, :conditions => ["session_id = ? AND page != ?", si, 'none']);
        if session_user.nil?
            row = [si, nil, nil, nil]
        else
            row = [session_user.username, si, session_user.survey_name]
        end
        pages.each do |p|
          a = 0
          b = 0
          c = 0
          d = 0
          e = 0
          allpages = PageEvent.where(:page => p, :session_id => si)
          allpages.each do |ap|
            a += ap.a
            b += ap.b
            c += ap.c
            d += ap.d
            e += ap.e
          end
          index = pages.index p
          end_index = (index + 1)*5 + 2
          if !p.nil?
            row[end_index] = a
            row[end_index-1] = b
            row[end_index-2] = c
            row[end_index-3] = d
            row[end_index-4] = e
          else
            row[end_index] = nil
            row[end_index-1] = nil
            row[end_index-2] = nil
            row[end_index-3] = nil
            row[end_index-4] = nil
          end
        end
      csv << row
    end
  end
end

如您所见,它从一个表生成一个csv文件,该表包含从一组调查中获取的每个单独页面上的数据。 问题是表中有约50,000个单独页面,并且heroku应用程序继续给我R14错误(内存不足512MB),并最终在一个小时后dyno进入睡眠状态时死亡。

话虽这么说,我真的不在乎要花多长时间,我只需要完成它即可。 我正在等待批准添加工人dyno来运行csv代,我知道这会有所帮助,但与此同时,我仍然希望优化此代码。 一次可能有超过100,000页的页面被处理,我意识到这是难以置信的大量内存,确实需要尽可能减少其内存使用量。 感谢您的时间。

您可以将其分成几批,以便合理地完成工作。

尝试这样的事情:

def self.to_csvAlt(options = {})

  # ...

  pages = PageEvent.order(:page).select(:page).map(&:page).uniq

  pages.find_each(:batch_size => 5000) do |p|
    # ...

使用find_each与batch_size,您将不会为循环做大的查找。 取而代之的是,它将获取5000行,运行循环,获取另一个,再次循环...等等,直到没有更多的记录返回为止。

这里要注意的另一项关键是,Rails不会实例化从数据库同时返回的所有对象,而只会实例化当前批处理中返回的对象。 如果您拥有庞大的数据集,则可以节省大量的内存开销。

更新:

使用#map将结果限制为模型的单个属性的效率非常低。 相反,您应该使用pluck Active Record方法直接从数据库中提取所需的数据,而不是使用Ruby处理结果,如下所示:

# Instead of this:
pages = PageEvent.order(:page).select(:page).map(&:page).uniq

# Use this:
pages = PageEvent.order(:page).pluck(:page).uniq

我个人更喜欢使用.distinct而不是别名.uniq因为我觉得它更符合数据库查询,而不是将东西与看起来更像数组函数的东西混淆:

pages = PageEvent.order(:page).pluck(:page).distinct

采用

CSV.open("path/to/file.csv", "wb")

这会将CSV流式传输到文件中。

而不是CSV.generate

generate将创建一个巨大的字符串,如果字符串太大,将最终浪费内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM