我在雪松堆上开发了一个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页,我意识到这是非常重要的内存,并且确实需要尽可能地减少其内存使用量。谢谢你的时间。
答案 0 :(得分:3)
您可以将其拆分为批次,以便以合理的方式完成工作。
尝试这样的事情:
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记录方法直接从DB中撤回您想要的数据,而不是使用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
答案 1 :(得分:2)
使用
CSV.open("path/to/file.csv", "wb")
这会将CSV流式传输到文件中。
而不是CSV.generate
。
generate
将创建一个巨大的字符串,如果它变得太大,最终将会释放内存。