【Rails】N+1問題とincludesメソッド
N+1問題とは
N+1問題は、データベースのクエリ実行回数(取得、更新、削除など)が増えることでパフォーマンスの低下を引き起こすことです。
具体例を挙げて説明します。
例えば、favorite(いいね)モデルとbookモデルが関連しているとします。
class Book < ApplicationRecord
has_many :favorites
end
class Favorite < ApplicationRecord
belongs_to :book
end
そして、次のコードでbook情報を取得する場合を考えます。
@books = Book.all
この場合、bookの情報を取得するために次のようなクエリがデータベースに発行されます。
SELECT * FROM books
ここで、@books
に含まれる各bookに対していいね情報を取得したい場合、次のようにループでアクセスすると、bookごとに個別のクエリが発行されることになります(N+1問題)。
@books = Book.all
@books.each do |book|
puts "#{book.title} - #{book.favorites.count}"
end
この場合、まずBook.all
でbookの一覧を取得するために1つのクエリが発生します。その後、ループ内で各bookのいいね数を取得するために、book.favorites.count
が実行されます。このループ処理により、bookの数だけ追加のクエリが発生し、N+1問題が発生します。つまり、bookの数が増えると、必要なクエリの数も増え、パフォーマンスの低下や遅延が発生する可能性があります。
N+1問題を回避するためには、Eager Loading(事前読み込み)を使用します。具体的には、関連するデータを1度のクエリでまとめて取得することで、N+1問題を回避します。これを解決するためにはincludes
メソッドを使います。
includesメソッド
includes
を使用すると、関連するいいね情報を一度のクエリで取得することができます。具体的には次のように書きます。
@books = Book.includes(:favorites)
この場合、book情報と関連するいいね情報が同時に取得されます。つまり、次のようなクエリがデータベースに発行されます。
SELECT * FROM books
SELECT * FROM favorites WHERE book_id IN (...)
この結果、@books
に含まれる書籍のいいね情報にアクセスする際には、追加のクエリが発行されず、既に取得された情報が使用されます。これにより、N+1問題が回避され、パフォーマンスが向上します。
つまり、includes
を使用することで、関連するデータを一括で取得することができます。これにより、データベースへのクエリ回数が減り、処理速度が向上します。
Discussion