Ruby on rails CSV解析占用了太多内存
我正在尝试读取一个5毫米的行文件,现在它超过了我在heroku上的内存使用量。我的方法有点快,每秒插入200次。。我认为这是对进口的冲击。。所以我的计划是成批进口1000或10000件。我的问题是如何判断我在文件的末尾,ruby有一个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
.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|