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

カッパでも分かるiOSアプリゲーム開発

カッパがひたすらゲーム制作に関することを書くブログです。Railsに関するTipsもたまにまとめてます。

N+1問題でパフォーマンス低下した時の対策

Rails
スポンサードリンク

f:id:InvokeTwoA:20151119141709j:plain

f:id:InvokeTwoA:20151023183618p:plain「N+1問題で重くなりページが開けない……」
f:id:InvokeTwoA:20151009145746p:plain「あたふた」

N+1問題とは?

たとえば User モデルがあったとする。
そのUserにFriendが1:Nでデータがあったとする。

Userをeachで回し、さらにその user ごとの friends 一覧の情報を取得したいとする。

普通にやったらUser数n, さらに friends m で n*m の膨大なSQLが発行されてしまい、
データが増えるごとにパフォーマンスが一気に低下してしまう。
これをN+1問題。

回避方法 includes を使う

下記のような感じで(recentは適当なscope)で、friendsに関連するデータを一気に引く事ができる

User.recent.includes(:friends)

SQLを見てると、users の回数分sqlを実行したりはせず、usersのデータを取得するタイミングで firends に関するデータを一括取得するSQLが発行される

scope 付きのincludes は指定できない?

こんな時よく思うのが、 friends に関して scope 付きのデータを取ってきたいということ。
例えば friends に active という scope があったとして

User.recent.includes(:friends.active)

のような感じでデータを取りたい場合があったりする。

残念ながら includes では scope 付きの指定はできない。
なんか良い方法がないか調査調査。

Bullet という gem を導入することで未然に防げる

N+1問題を検知した時、ここに includes を入れたら?とかログに出力してくれる。
開発環境ではポップアップでN+1問題が発生してますとかアナウンスしてくれたりするので非常に便利。