[英]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.