RailsのN+1問題について
N+1問題とは?
N+1問題とは、データベースからデータを取得する際に不必要に多くのクエリが発行されるという問題。
例えば、User
モデルがあり、それに紐づくPost
モデルがあるとする。
class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
end
次に、すべてのユーザーとその投稿を表示するコードを考える。
users = User.all
users.each do |user|
puts user.name
user.posts.each do |post|
puts post.title
end
end
このコードは、1回のクエリでusers
を取得し、その後各ユーザーに対して別のクエリを発行してposts
を取得する。もしusers
テーブルに100人のユーザーがいれば、このコードは101回のクエリを発行する(1回はUser.all
、100回は各user.posts
)。
- パフォーマンスの低下: クエリが多いと、その分データベースへのアクセス時間が増える。
- リソースの浪費: 不必要に多くのクエリを発行することで、データベースサーバーに負荷をかける。
解決方法
includes
Railsで簡単にN+1問題を解決する方法の一つは、includes
メソッドを使うこと。
users = User.includes(:posts)
users.each do |user|
puts user.name
user.posts.each do |post|
puts post.title
end
end
このコードでは、2回のクエリ(1回はusers
を取得、もう1回は関連するposts
を一度に取得)で必要なすべてのデータが取れる。
preload
とeager_load
includes
以外にもpreload
やeager_load
メソッドがある。
preload
: 関連するレコードを別々のクエリでロードする。eager_load
: 関連するレコードをLEFT OUTER JOIN
でロードする。
# preloadを使用する例
users = User.preload(:posts)
# eager_loadを使用する例
users = User.eager_load(:posts)
bullet gem
bulletをインストールすると、N+1問題が発生する可能性があるコードを検出してくれる。
ただし、完璧ではないので自分で確認するべき。