我正在尝试从数据库中读取大量单元格(超过100.000)并将它们写入VPS Ubuntu服务器上的csv文件。碰巧服务器没有足够的内存。
我正在考虑一次读取5000行并将它们写入文件,然后再读取5000行等。
我应该如何重构当前代码,以便不会完全消耗内存?
这是我的代码:
def write_rows(emails)
File.open(file_path, "w+") do |f|
f << "email,name,ip,created\n"
emails.each do |l|
f << [l.email, l.name, l.ip, l.created_at].join(",") + "\n"
end
end
end
该函数由sidekiq worker调用:
write_rows(user.emails)
感谢您的帮助!
答案 0 :(得分:4)
这里的问题是,当你调用emails.each
ActiveRecord加载数据库中的所有记录并将它们保存在内存中时,为避免这种情况,你可以使用方法find_each
:
require 'csv'
BATCH_SIZE = 5000
def write_rows(emails)
CSV.open(file_path, 'w') do |csv|
csv << %w{email name ip created}
emails.find_each do |email|
csv << [email.email, email.name, email.ip, email.created_at]
end
end
end
默认情况下find_each
一次加载1000个批次的记录,如果要加载5000个记录的批次,则必须将选项:batch_size
传递给find_each
:
emails.find_each(:batch_size => 5000) do |email|
...
有关find_each
方法(以及相关find_in_batches
)的更多信息,请访问Ruby on Rails Guides。
我使用CSV
类来编写文件,而不是手工连接字段和行。这并不是性能优化,因为在文件上写入不应该成为瓶颈。