我正在尝试将大量数据从数据库导出到csv文件,但这需要很长时间,并且担心我会遇到重大内存问题。
有没有人知道在没有内存累积的情况下导出CSV的更好方法?如果是的话,你能告诉我怎么样吗?感谢。
这是我的控制器:
def users_export
File.new("users_export.csv", "w") # creates new file to write to
@todays_date = Time.now.strftime("%m-%d-%Y")
@outfile = @todays_date + ".csv"
@users = User.select('id, login, email, last_login, created_at, updated_at')
FasterCSV.open("users_export.csv", "w+") do |csv|
csv << [ @todays_date ]
csv << [ "id","login","email","last_login", "created_at", "updated_at" ]
@users.find_each do |u|
csv << [ u.id, u.login, u.email, u.last_login, u.created_at, u.updated_at ]
end
end
send_file "users_export.csv",
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=#{@outfile}"
end
答案 0 :(得分:7)
您正在构建一个巨大的字符串,因此您必须将整个csv文件保留在内存中。您还要加载所有用户,这些用户也会占用大量内存。如果你只有几百或几千名用户,那么你将可能需要做两件事情,这没有任何区别
使用
User.find_each do |user|
csv << [...]
end
这会批量加载用户(默认为1000)而非所有用户。
您还应该考虑将csv写入文件而不是缓存内存中的所有内容。假设您已创建临时文件,
FasterCSV.open('/path/to/file','w') do |csv|
...
end
将您的csv写入文件。然后,您可以使用send_file
发送它。如果您已打开文件,FasterCSV.new(io)
也应该有效。
最后,在rails 3.1及更高版本上,您可以在创建csv文件时对其进行流式传输,但这不是我之前尝试过的。
答案 1 :(得分:1)
除了有关csv生成的提示之外,请务必优化对数据库的调用。 只选择您需要的列。
@users = User.select('id, login, email, last_login, created_at, updated_at').order('login')
@users.find_each do |user|
...
end
如果您有1000个用户,并且每个用户都有密码,password_salt,city,country,... 然后从数据库转移了少量1000个对象,创建为ruby对象,最后进行垃圾收集。