【Rails】大量のデータを処理できるfind_eachの使い方

eachで数万レコードのデータを扱うとメモリ不足でサーバーが落ちてしまします。特に無料のサーバーだとメモリが少ないので、簡単にサーバーが落ち大変なことになります。

find_eachの使い方をマスターすれば、少ないメモリで大量のデータを扱えるようになります。今回はfind_eachの使い方を詳しく解説します。

目次

  1. find_eachメソッドとは
  2. find_eachメソッドの基本的な使い方
  3. find_eachのオプションの使い方
  4. find_eachを使う際の注意点

1.find_eachメソッドとは

find_eachメソッドとは、分割してデータを取得して処理を行うメソッドです。これに対し、eachは全てのデータを取得して処理を行います。

デフォルトでは1000件づつ取り出して処理を行います。これによりメモリの消費を抑えることができます。

何件ごとに分割して処理するか(batch_size)、何件目から処理を始めるかは(start)、オプションで指定することができます。

2.find_eachメソッドの基本的な使い方

find_eachは以下のように使用します。

モデル.find_each([オプション]) do |i|
  処理
end

例えば、Userモデルに何らかの処理を行う場合

User.find_each do |u|
  u.do_something
end

といった感じで使用します。

whereと組み合わせることもできます。例えば20歳以上のみを、1000件ごとに分けてする場合には以下のような書き方になります。

User.where("age >= 20").find_each do |u|
  u.do_somethong
end

3.find_eachのオプションの使い方

find_eachでは、「何件ごとに分割して処理するのか(:batch_size)」と「何件目から処理を始めるのか(:start)」の2つをオプションとして指定できます。

:batch_sizeオプションは、デフォルトでは1000件に設定されています。つまりデータベースから1000件づつ取り出して処理しています。

件数を増やすことでデータベースへの問い合わせ回数が減り、処理スピードをあげることができます。メモリの使用量も増えることになるので、パフォーマンスを細かくチューニングしたい際に利用しましょう。

:startオプションは、開始する位置を指定します。例えば100を指定すれば100件目から処理を開始します。

:startと:batch_sizeを使う場合、以下のような書き方になります。

User.find_each(start:100,batch_size:2000) do |u|
  u.do_something
end

4.find_eachを使う際の注意点

find_eachを使用する場合には2点注意する必要があります。

  • orderが使用できない
  • limitが使用できない

つまり User.order(created_at: :desc).find_eachやUser.limit(1000).find_eachといった書き方は無視されます。

orderは、1000件毎に分割して処理するためにidで昇順に並び替えているために使えないようになっています。また、idが整数出ないモデルに対しても使用できません。

limitも同様に、batch_sizeの管理にlimitを使用しているため無視されるようになっています。

以上でfind_eachの解説を終わります。メモリを節約して、効率的なシステムを作れるようにしましょう。