Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails CSV解析占用了太多内存_Ruby On Rails_Ruby_Csv_Activerecord_Activerecord Import - Fatal编程技术网

Ruby on rails CSV解析占用了太多内存

Ruby on rails CSV解析占用了太多内存,ruby-on-rails,ruby,csv,activerecord,activerecord-import,Ruby On Rails,Ruby,Csv,Activerecord,Activerecord Import,我正在尝试读取一个5毫米的行文件,现在它超过了我在heroku上的内存使用量。我的方法有点快,每秒插入200次。。我认为这是对进口的冲击。。所以我的计划是成批进口1000或10000件。我的问题是如何判断我在文件的末尾,ruby有一个.eof方法,但它是一个文件方法,我不确定如何在循环中调用它 def self.import_parts_db(file) time = Benchmark.measure do Part.transaction do

我正在尝试读取一个5毫米的行文件,现在它超过了我在heroku上的内存使用量。我的方法有点快,每秒插入200次。。我认为这是对进口的冲击。。所以我的计划是成批进口1000或10000件。我的问题是如何判断我在文件的末尾,ruby有一个
.eof
方法,但它是一个
文件
方法,我不确定如何在循环中调用它

    def self.import_parts_db(file)
        time = Benchmark.measure do
            Part.transaction do 
                parts_db = []
                CSV.parse(File.read(file), headers: true) do |row|
                    row_hash = row.to_hash
                    part = Part.new(
                        part_num: row_hash["part_num"], 
                        description: row_hash["description"], 
                        manufacturer: row_hash["manufacturer"],
                        model: row_hash["model"],
                        cage_code: row_hash["cage_code"],
                        nsn: row_hash["nsn"]
                        )
                    parts_db << part
                end
                Part.import parts_db
            end
        end
        puts time
    end
def self.import\u parts\u db(文件)
time=Benchmark.measure do
第三部分:交易做什么
部分_db=[]
CSV.parse(File.read(File),headers:true)do |行|
行\u散列=行到\u散列
part=part.new(
part_num:row_hash[“part_num”],
description:row_hash[“description”],
制造商:行_散列[“制造商”],
模型:行_散列[“模型”],
笼码:行散列[“笼码”],
nsn:行\u哈希[“nsn”]
)
第1部分问题
当您对一个大文件使用
File.read(File)
时,您的脚本将占用大量内存(可能太多)。您将整个文件读入一个巨大的字符串,即使
CSV
逐行读取

当您使用具有数千行的文件时,它可能工作正常。不过,你还是应该使用。 改变

在这个例子中,内存使用从1GB增加到了0.5MB

第二个问题
parts\u db
成为一个庞大的部件数组,它一直在增长,直到CSV文件的末尾。 您需要删除事务(导入将很慢,但所需内存不会超过一行),或者分批处理CSV

这里有一种可能性。我们再次使用
CSV.parse
,但仅对2000行的批次使用:

def self.import_parts_db(filename)
  time = Benchmark.measure do
    File.open(filename) do |file|
      headers = file.first
      file.lazy.each_slice(2000) do |lines|
        Part.transaction do
          rows = CSV.parse(lines.join, write_headers: true, headers: headers)
          parts_db = rows.map do |_row|
            Part.new(
              part_num: row_hash['part_num'],
              description: row_hash['description'],
              manufacturer: row_hash['manufacturer'],
              model: row_hash['model'],
              cage_code: row_hash['cage_code'],
              nsn: row_hash['nsn']
            )
          end
          Part.import parts_db
        end
      end
    end
    puts time
  end
end
第三个问题? 前面的答案不应该占用太多内存,但导入所有内容可能需要很长时间,对于远程服务器来说可能太多了

使用枚举器的优点是可以轻松跳过批处理,并获得所需的批处理

假设导入时间太长,并且在424000次成功导入后由于某种原因停止

您可以替换:

file.lazy.each_slice(2000) do |lines|

跳过前424000个CSV行,然后分析接下来的300000个CSV行

对于下一次导入,请使用:

file.lazy.drop(424_000+300_000).take(300_000).each_slice(2000) do |lines|
然后:

file.lazy.drop(424_000+2*300_000).take(300_000).each_slice(2000) do |lines|

CSV.parse
非常有效,它将一个解析的CSV行传递给执行处理的块。
问题不是来自CSV解析器,而是来自于在内存中构建
parts\u db
数组。我建议重写
部分。import
方法逐行导入数据,而不是一次导入整个记录数组。

尝试其他CSV。有一个大约是30兆,使用了剩余的8兆内存,重新保存文件似乎已经纠正了我的问题。

但上面是您推荐的
。foreach
。。它在这个实例中不起作用吗?
CSV.foreach
不会返回可枚举的值,这是我们需要的
每个片段的值。只要不使用
File.read()
,就可以使用
CSV.parse
。在第二种情况下,它仅适用于2000行。我使用了您的示例,在424000次导入之后,我从heroku控制台得到了一个
ETIMEDOUT:read ETIMEDOUT
错误。你知道我该怎么做吗?我没有和Heroku打交道的经验。这有用吗?我看到了,但我想不是这样的。我认为这是完成这项任务所花费的时间。谢谢你,你帮了我大忙!
file.lazy.drop(424_000).take(300_000).each_slice(2000) do |lines|
file.lazy.drop(424_000+300_000).take(300_000).each_slice(2000) do |lines|
file.lazy.drop(424_000+2*300_000).take(300_000).each_slice(2000) do |lines|