优化读取数据库并写入csv文件

Alj*_*jaz 6 ruby csv ruby-on-rails sidekiq

我正在尝试从数据库中读取大量单元格(超过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
Run Code Online (Sandbox Code Playgroud)

该函数由sidekiq worker调用:

write_rows(user.emails)
Run Code Online (Sandbox Code Playgroud)

感谢帮助!

tor*_*o2k 4

这里的问题是,当您调用emails.eachActiveRecord 时,会从数据库加载所有记录并将它们保存在内存中,为避免这种情况,您可以使用以下方法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
Run Code Online (Sandbox Code Playgroud)

默认情况下find_each一次加载 1000 条记录,如果您想加载 5000 条记录,您必须将选项传递:batch_sizefind_each

emails.find_each(:batch_size => 5000) do |email|
  ...
Run Code Online (Sandbox Code Playgroud)

find_each有关该方法(以及相关方法)的更多信息可以在Ruby on Rails 指南find_in_batches中找到。

我使用该类CSV来编写文件,而不是手动连接字段和行。这并不是性能优化,因为写入文件不应成为此处的瓶颈。