LoginSignup
13
2

【Rails】find_eachで大量データを扱う

Last updated at Posted at 2024-03-28

この記事で言いたいこと

大量データのループ処理を扱う際にはfind_eachがメモリ節約に有効である。

きっかけ

先日、Ruby on Railsで大量データを扱う機会がありました。
その際find_eachというメソッドがあることを学んだので、eachとの違いを明らかにしながらまとめていこうと思います。

eachを用いた例

Studentというモデルがあると仮定します。
studentsテーブルにある生徒の名前(nameカラム)を全員分出力したい場合、皆さんはどのようなコードを書きますか?

Student.all.each do |student|
    puts student.name
end

上記のようにStudent.allでレコードを全件取得してからeachメソッドを使用することで、studentsテーブルにいるすべての生徒の名前を出力することができます。

レコード数が少ない場合は上記の例のように書くのがよいでしょう。
しかしレコードが大量にある場合は、上記のようにレコードを一気に取得する方法だとメモリを圧迫してしまいます。

ここで満を持してfind_eachの登場です。

find_eachを使ってみる

find_eachのメリットは「分割してレコードを取得できる」ことです。

以下に例を示します。

Student.find_each(batch_size: 500) do |student|
    puts student.name
end

引数のbatch_sizeは1回あたりに取得するレコード数を示しています(デフォルトでは1000件)。
はじめにレコードを500件取得してループ、また次の500件を取得してループ...といった様子です。
出力結果は前述のeachを用いた例と全く同じです。

さらに理解を深めるため、両者で発行されるSQLを比較してみます。

SQLの比較

findの場合

SELECT * FROM students

Student.allを使用しているため、一気に全レコードを取得しています。

find_eachの場合

-- 1回目
SELECT * FROM students ORDER BY student_id ASC LIMIT 500

-- 2回目(ループ処理後)
SELECT * FROM students WHERE student_id > 500 ORDER BY student_id ASC LIMIT 500

-- 3回目(ループ処理後)
SELECT * FROM students WHERE student_id > 1000 ORDER BY student_id ASC LIMIT 500

-- 以後同様に続く...

(student_idはstudentsの主キーです )

  • 主キーでソートする
  • 取得できるレコードの取得数の上限をbatch_size個にする
  • 2回目以降は(主キー)>(前回取得したレコードのうち最大の主キー)の条件を付与する

上記の仕組みによってレコードが分割して取得できていることがわかりました。

まとめ・感想

冒頭でも述べたように、大量データを扱う際にはメモリに気を遣う必要がありfind_eachは有効な手段です。
今回紹介した以外にもfind_eachは取得したいレコードの始点・終点の主キーを指定できたり、降順に並べたりすることもできます。また、似たようなメソッドにfind_in_batchesというものもあります。
気になる方は是非調べてみてください!

参考

Railsドキュメント(find_each)
Railsドキュメント(find_in_batches)
Railsガイド

13
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
2