csvファイルのインポート機能を実装している際に生じた疑問が今回のお題です。
備忘録として記述しております。
eachメソッド
eachは、配列の要素ひとつひとつに対して、配列の要素の数だけ繰り返し処理が行えるメソッドです。
eachメソッドの問題点
DBやファイルからデータを取得してきて処理をしたい場合、
eachメソッドで処理しようとすると対象データがすべてメモリに展開されてしまうのだそうです。
【出力時の対策】find_each や find_in_batchesを使う。
find_eachメソッドは1行ずつメモリに展開していくので、
レコード数を気にせず処理をすることができるそうなのです!
Stock.each do |stock|
# 処理
end
こちらを・・・
Stock.find_each do |stock|
# 処理
end
に変更です!
さらにさらに、find_in_batchesメソッドだと取得したデータを配列でまとめて処理してくれるそうです!
デフォルトで1000件(!)ずつ処理してくれて
batch_sizeオプションで何件ずつ処理するかの指定もできちゃうそうです!
Stock.find_in_batches(batch_size: 500) do |stocks|
# 処理
end
ちなみに、batchはバッチと読んで「一束、一団、1回分、ひとまとまり」と言う意味で
まとめてしないさいな!って事ですね。
【入力時の対策】foreachを使用する
私がインポートしようとしているcsvファイルで4~500行ぐらいですが
数千行、数万行にも及ぶcsvファイルを一度に展開してしまうと鬼の如くメモリを消費してしまいます。
(数万行ってなんすか、おっかねぇ・・・)
対策として、foreachを使って1行ずつ展開してメモリを節約します。素晴らしい!
stocks = CSV.read('tmp/stock_data.csv') # 一度に展開されてしまう
users.each do |row|
# 処理
end
こいつが・・・
CSV.foreach('tmp/stock_data.csv') do |row| # 1行ずつ展開してメモリ節約!
# 処理
end
こうだ!
素晴らしいですね。考えついた人・形にした人は神様に思えます。
参考にさせていただいたサイト様
https://techblog.lclco.com/entry/2019/07/31/180000
ありがとうございます!