【问题标题】:Generating CSV/Excel file for 100 Million of records in Ruby on rails?在 Ruby on rails 中为 1 亿条记录生成 CSV/Excel 文件?
【发布时间】:2019-10-15 03:58:49
【问题描述】:

需求是这样的

我们从数据库中获取庞大的数据集(> 10亿条记录),需要将其导出为csv文件或excel。

当前实现使用 CSV 类 CSV.generate

 CSV.generate(headers: true) do |csv|
    csv << header
    @obj.find_each do |c|
     arr = [c.id,c.name,soon]
     csv << array
    end
 end

并将输出发送到

Zip::File.open(file, Zip::File::CREATE) do |zip|
        zip.get_output_stream("test.#{@format}") { |f| f.puts(convert_to_csv) }
      end

所有这些操作都是由其他延迟的工作完成的 这在记录

我的想法是将记录分块,例如将 100 万行分成 50 个文件 (100 万/20000)(csv1.csv,csv2.csv,csv3.csv,csv4.csv,csv5.csv),然后 concat将它们放入单个文件或将所有文件压缩在一起(更快)

谁能告诉我如何开始。

【问题讨论】:

  • 请问csv数据会在哪里使用?因为我认为 Excel 最多支持 100 万行,请参阅 stackoverflow.com/questions/23757991/…。对于这么多数据,您最好使用某种数据库转储格式。
  • 在这种情况下,我首先要考虑的事情之一是消除不必要的 ORM 开销并使用对 DB 的较低级别的查询——实例化数十亿个 AR 实体并没有太大意义从中获取 3 个属性 - 使用原始数据集将减少内存使用并显着提高性能...
  • 旁注: 1million/20000 是 50,而不是 5 顺便说一句。另外,SELECT INTO OUTFILE.
  • @AlekseiMatiushkin 不知道这个功能,谢谢!类似的,但对于 postgres stackoverflow.com/questions/1517635/…

标签: ruby ruby-on-rails-4 rubygems


【解决方案1】:

这取决于是否

     arr = [c.id,c.name,soon]

需要在 Ruby 中计算,或者你可以用 SQL 重写它。

  • 如果您必须将其保留在 Ruby 中,您可以尝试避免 ActiveRecord 开销并改用raw query。您可能必须自己实现分块处理
  • 否则,您可以查看一些用于 CSV 导出的数据库原生工具。例如,对于 MySQL,类似于 SELECT INTO OUTFILEmysql

【讨论】:

    【解决方案2】:

    以块和find_in_batchespluck 的形式写入CSV。比如:

    Model.pluck(:id, :name, ...).find_in_batches(10_000) do |ary|
      CSV.open("tmp.csv", "ab") do |csv|
        csv << ary.map{|a| a.join ','}.join("\n")
      end
    end
    

    【讨论】:

    • OP 正在使用 find_each,我相信你知道,它使用 find_in_batches 然后迭代批次并产生结果。 find_each 的关键在于避免内存问题,我已经非常成功地将它用于更大的数据集。 OP建议是有问题的。它不像你简单断言的那样“明显是 ActiveRecord”。
    • 如何提高性能,因为 find_each 也分批迭代 (:1000)?
    • 因为 CSV 超出范围并且不使用大量内存。在 OP 的示例中,整个 CSV 保存在内存中。
    【解决方案3】:

    查看 CSV.generate 的源代码给我的印象是 csv 数据保存在内存中,而内容正在累积。这似乎是一个很好的优化目标,尤其是当您看到内存随着数据集线性扩展时。由于您的数据非常简单,您可以跳过 CSV 并直接转到文件吗?您可以更好地控制数据何时刷新到磁盘。

    File.open("my.csv") do |file|
      file.puts '"ID","Name","Soon"'
      @obj.find_each do |c|
        file.puts "\"#{c.id}\",\"#{c.name}\",\"#{c.soon}\""
        # flush if necessary
      end
    end
    

    您需要先写入磁盘,然后稍后使用此方法压缩结果。

    【讨论】:

    • 瓶颈显然是ActiveRecord,而不是CSV。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多