#Rails ActiveRecordのアンチパターン
##はじめに
急にスケジュールが詰め詰めになったので、残り4日とわずかですが、今回は少なめです。すみません...
頑張ります。
今回はActiveRecordにおけるアンチパターンについて。パフォーマンスを落とすようなコードを書かないようにアンチパターンを日々、覚えるようにしましょう。
##All Each Pattern
User.all
などでレコードを全取得してそこからeachで回すパターン。
User.all.each do|user|
if user.created_at >= Date.new(2018)
user.point += 100
user.save
end
end
2018年以降に登録してくれたユーザにポイントをプレゼントしよう的なコード(ほぼ参考元のコードです。すみません)
ダメなとこ
1,Userの全件取得でメモリを圧迫。
2,eachメソッドにより取得したユーザー全件を一気にメモリに展開してから処理するので、こちらもよろしくない。
改善策
1,そもそも全件取得する必要がない。where
を使って取得件数を下げる。
2,find_eachメソッドを使って、分割して処理する。(find_eachメソッドはeachメソッドと違って、一気にではなく分割して処理する。)
##改善後
User.where("created_at >= ?", Date.new(2018)).find_each do |user|
user.point += 100
user.save
end
よくなりました...と思いきや、まだ問題があります。
##N+1 update queries pattern
前にN+1問題は書きましたね。↓
RailsのN+1問題の解決方法 初心者→中級者へのSTEP9/25
似たような感じの問題です。1回のselectに対し、N回のupdateが起きています。1回にしましょう。
→update_all
を使う。
##再改善後
User.where("created_at >= ?", Date.new(2018)).update_all("point = point + 100")
以前書きましたが、update_all
はバリデーションをしませんので今回は問題ないですが、パフォーマンスが上がる!と言っってむやみに使ってはダメです。
これでパフォーマンスがぐぐっと上がるはずです。
##まとめ
なんかほぼ自分のブログみたいになってきたこのカレンダー。お家がゴタゴタして書く暇あるか微妙ですが、残り数日休まず記事をあげたいです。今回のアンチパターンは参考元のスライドが非常にわかりやすいです。興味持ったらぜひそちらを読んでください。他の例も載せてくださってます。
##参考にしたの
ActiveRecordデータ処理アンチパターン
https://speakerdeck.com/toshimaru/active-record-anti-patterns?slide=26
簡単に始められるRailsパフォーマンス改善
https://qiita.com/yu-croco/items/d7a39b34036d638c5026