読者です 読者をやめる 読者になる 読者になる

hotoolong's blog

Railsやvimや気になったことを綴ってます

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をかける。
レコード数が多くなったときに利用してみると効果的。