N+1問題について、なかなか理解できなかったのですが
今日やっと理解できたので、残しておきたいと思います。
-
N+1問題はデータベースの話
-
N+1問題は「includes」によって解消できる
-
「includes」はモデルに対して使うメソッド
-
コントローラーのアクション内で、データベース(テーブル)からレコードを取得する際に使う
-
前提としてアソシエーションが組まれている必要がある
1.N+1問題はデータベースの話
まずはじめに、N+1問題とは何なのかについて説明していきます。
N+1問題とは端的に言うと、SQLがたくさん発行されてしまい動作が遅くなる問題のことです。
アプリケーションの作成において重要なのが、
きちんとした仕組みや見やすいレイアウトを作成することももちろんですが、
できるだけ動作が重くならないように努力することも大切です。
データベース(以下DB)とのやりとりが必要なアプリケーションの場合、
「いかに少ない工程で多くのデータを取ってこれるか」というのが大切、ということですね。
(例えばtwitterで言うと、タイムラインでいろんなユーザーのツイートが順番に表示されていますが、これは、ユーザーがツイートをした時に一度DBに保存されたものを、ツイート一覧のページでDBから呼び出している、という状態にあります)
ここで、必要なデータをDBから取ってくる際にSQLというコードが発行されますが、
このSQLの量が最小限よりも1回多くなってしまっている状態のことを、
「N+1問題が発生している」と表現します。
まとめると、「もっと速くデータを取ってこれるのに(少ない量のSQLでできるのに)無駄な動作が発生しちゃってるよ〜」ってことです。
2.N+1問題は「includes」で解消できる
次に、N+1問題の解消の仕方です。
SQLの発行を少なくする、すなわちデータベースとのやりとりを最小限にすればいいのだから、
「このデータをDBから取ってくる時に、あっちのデータも含めて(include)持ってきてよ〜!」
という指示が、「include」によって行えます。
3.「includes」はモデルに対して使うメソッド
「includes」を使う時は、具体的には以下のように記述します。
Tweet.includes(:user)
データベースとのやりとりということは、「includes」はモデルに対して使うメソッドになります。
先の例では、タイムラインで「いろんなツイート」と「そのツイートをしたユーザー」のデータを取得してくる必要があるので、
ツイートを取ってくる時にユーザーもincludesしちゃえば良いわけです。
4.コントローラーのアクション内で、データベース(テーブル)からレコードを取ってくる際に使う
次は「Tweet.includes(:user)」をどこに書くかという問題ですが、
①最終的にデータを表示するのはタイムライン(ビュー)
であり、
②DBから取ってきた情報をビューで表示できるように、インスタンス変数に代入する必要がある
ので、
データを表示させたいビューを呼び出すアクション内に定義します。
5.前提としてアソシエーションが組まれている必要がある
上記のようにincludesを定義することで、複数のテーブルからレコードを取得することができますが、
そもそもテーブル同士が紐付けられている必要があります。