N+1問題:データベースからデータを取り出す際に、大量にSQLが発行されて
動作が遅くなってしまう問題のことです。SQLとはデータベースとのやりとりをする際に用いるデータベース型言語(プログラミング言語ではないので注意)です。
実際にN+1問題が発生している様子を見ていきましょう。
N+1問題は1対多のアソシエーションの場合に多くみられるので、投稿一覧ページを見ていきたいと思います。
投稿一覧ページを表示した際のターミナルです。
見てわかる通り、多くのSQL文が発行されています。
いろいろな機能をつけていてSQL文が複雑になっているので簡単な例を用いて説明します。
def index
@posts = Post.all #データベース上から投稿データを取得
end
投稿一覧ページに関するメソッド定義です。
<% @posts.each do |t| %>
<%= t.user.name %>
<% end %>
投稿一覧のviewの定義です。
この場合、まず、
(1)投稿内容の全件取得を行います。 1回
(2)N個ある投稿一つ一つに紐づいているユーザーを取得してくる N回
合計でN+1回の処理を行っている。つまり、投稿データの個数Nの値が膨大になるにつれて、それに紐づいているUserテーブルへのアクセス回数も膨大となります。
このようなN+1問題の対策の一つとして
def index
@posts = Post.all #データベース上から投稿データを取得
end
の部分を
def index
@posts = Post.includes(:user)
end
に変えると良い。
includes()メソッドを用いることで、投稿に紐づいているユーザーを一回で全て取得することが可能となり、処理速度を改善することが可能となる。
また投稿一覧の表示個数を分割することで処理速度を改善することができると考えられる。それに関してはまた次回。