3

データベースから 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
4

2 に答える 2

7

1 つの巨大な文字列を構築しているため、csv ファイル全体をメモリに保持する必要があります。また、大量のメモリに座っているすべてのユーザーもロードしています。数百または数千のユーザーしかいない場合は違いはありませんが、おそらく2つのことを行う必要があるポイントがあります

使用する

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 ファイルをストリーミングできる可能性がありますが、これは以前に試したことはありません。

于 2012-06-27T07:46:19.927 に答える
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 オブジェクトとして作成され、最後にガベージ コレクションが行われます。

于 2012-06-27T09:53:41.053 に答える