hotoolong's blog

プログラムのことやエンジニアリングに関することを記事にしています。

find_in_batchesとupdate_allで大量のレコードを分割処理する

最近はバックエンドの処理を捌く場合に、Railsを意識してるとなかなかパフォーマンスが出なかったのが、
find_in_batchesとupdate_allの組み合わせで意外とパフォーマンスが出たので、メモメモ。

今回の件は、かなりデータ数が多くなりすぎて絞り込んでもLockしてる時間が長すぎる場合に有効。

普通のupdate_all

Model.update_all("value = (a * 0.1) + ( b * 0.2)")

上記はupdate_allで処理すると全件もしくはある程度の絞り込みができるが、当然ながらLockされてる時間を考えると厳しい場合がある。

普通のfind_in_batches

Model.find_in_batches(:batch_size => 5000, ) do |models|
  Model.transaction do
    models.each do |model|
      model.value = model.a * 0.1 + model.b * 0.2
      model.save!
    end
  end
end

上記の場合はコミットは5000件でやるけど、UPDATE文が1件ずつになるから件数多いと時間がかかる。

find_in_batches と update_all を組み合わせる

Model.find_in_batches(:batch_size => 5000) do |models|
  Model.update_all("value = (a * 0.1) + ( b * 0.2)", ["id in (?)", models.map{|m|m.id}])
end

上記のようにキーを条件に大量に投入することで、件数指定でupdateをかける。
レコード数が多くなったときに利用してみると効果的。