本文へスキップ
Rails

Rails の大量 CSV 出力を速くする ── N+1・バッチ読み込み・ストリーミング

タイムアウトしがちな CSV ダウンロードを、バックグラウンド処理に移さずに高速化するための確認ポイントと実装例です。

管理画面の CSV ダウンロードは、データが増えるにつれて遅くなり、やがてタイムアウトする代表的な機能です。非同期処理に移す前に、まずは同期のまま改善できる余地がないかを確認します。

遅くなる主な原因

実装例

def export
  headers["Content-Type"] = "text/csv; charset=utf-8"
  headers["Content-Disposition"] = %(attachment; filename="orders.csv")
  headers["X-Accel-Buffering"] = "no"

  self.response_body = Enumerator.new do |out|
    out << "\uFEFF" # excel で文字化けしないよう BOM を付ける
    out << CSV.generate_line(%w[注文番号 顧客名 金額 注文日])
    Order.where(created_at: range).in_batches(of: 2_000) do |batch|
      batch.joins(:customer)
           .pluck(:number, "customers.name", :total, :created_at)
           .each { |row| out << CSV.generate_line(row) }
    end
  end
end

in_batches で一定件数ずつ読み込み、pluck で必要な列だけを配列として取得することで、オブジェクト生成のコストを大きく減らせます。Enumerator をレスポンスにすると生成した行から順に送信されるため、最初のバイトが届くまでの時間も短くなります。

効果を確かめる

まずは30分、
日本語でお気軽にご相談ください。

「こんなことはできますか?」という段階でも構いません。1営業日以内にブリッジSEからご返信します。無理な営業は一切いたしません。

メールで相談無料相談