<DAY79> n + 1 問題について
学習状況
●学習日数 79日
●学習時間(本日) 10時間
●累計学習時間 267.0時間
●一日あたりの平均学習時間 3.38時間
n + 1 問題とは
データを呼び出す際に大量のSQLが発行されてしまう問題。 モデルを利用してデータベースの情報にアクセスする際に、SQLが発行される。 SQLが発行されるたびにデータベースに対して通信が走るので、SQLが大量に発行されれば処理が重くなる。
includesメソッドを使って解決
railsで用意されている、データーベースにアクセスするためのメソッドです。allとか一緒のくくりです。 指定したモデルを結合するよって意味。
@tweets = Tweet.includes(:hoge) 結合したいモデル名
例を出してみる
A君は、あるツイートに対して5件コメントしている。(自分のツイートに5回もコメントしているのが気になるけど) テーブル名は、usersが正しいです。
コードの流れを整理して考えると、 URLのデータが飛ぶ。 rotesの定義に従い対応するコントローラが動く。 コントローラの指示に基づき、モデルが動く。 モデルが指示を翻訳しデータベースにアクセスする。 モデルが取得したデーターをコントローラに返す。 対応するビューを開く。 ビューで記載されているrubyのコードが動く
発生した箇所について
対応するtweetsコントローラのshowアクションには、
@tweet = Tweet.find(params[:id]) @comments = @tweet.comment
対応するレコードのツイート情報取得し、コメントテーブルにあるすべてのデーターを加えて、変数@commentsに格納しています。
次にビューを見て見ます。
<% @comments.each do |comment| %> <p> <%= comment.user.nickname %> <%= comment.text%> </p> <%end%>
each文を使って取り出しています。 モデルから持ってきた情報は配列形式になっているんです。 アソシーエションを使って、usersテーブルに入っているnicknameを取得しています。 モデルクラスはuserだけど、テーブルはusersです。 userクラスから情報を取得する時に、n+1問題が発生しています。
eachで定義されたcomment変数には、usersテーブルの情報はありません。 結合しないと、5件のコメントに対してuser_idを取得する動作を繰り返す事になります。 includes(:user)とする事で、@tweet.commentと結合します。 よって、SQLの発行は1回だけで済むわけです。
解決
コントローラーの部分だけ見ると、なぜuserモデルと結合するのかわからないですが、ビューデータやりとりを把握すれば理由が明確になりました。
@tweet = Tweet.find(params[:id]) @comments = @tweet.comment.include(:user)