0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RailsのN+1問題をincludesメソッドで解決する

Posted at

アプリの概要(前提)

Image from Gyazo

・こんな感じで、ユーザーaaaの投稿に対してコメントが6件ついていて、そのコメントと共に、コメントした人のユーザー情報(名前とプロフ画像)が載っている

/posts/8にGETリクエストを送るとviews/posts/show.html.slimが返される

・ユーザーaaaのid16
・ユーザーoooのid17

・Commentはbodyカラムを、Userはnameカラムとimageカラムを持つ

N+1問題とは

N+1問題とは、データベースからデータを取り出す際に、必要以上にSQLが発行されることで、パフォーマンスが悪くなる(処理速度が遅くなる)問題のこと

具体例

先程のアプリのpostsコントローラー

posts_controller.rb
def show
    @post = Post.find(params[:id])
    @comments = @post.comments.all.order(created_at: :desc)
end
view
/ 投稿一覧

/ コメント一覧
<% @comments.each do |comment| %>
  <%= comment.user.name %>
<% end %>

<% @comments.each do |comment| %>のところでpost_id: 8の全てのComment情報(body)及びコメントしたUserの情報(nameとimage)のSQLが発行される(allメソッド実行時ではないから注意)

その時に発行されたSQL(rails sの画面で確認できる)
Image from Gyazo

まず、オレンジ色の線が
post_id: 8Commentを全件取得(今回は6件)
次に、赤色の線が
1番目のcommentに紐づいているuserを1人取得(id: 17)
2番目のcommentに紐づいているuserを1人取得(id: 17)
3番目のcommentに紐づいているuserを1人取得(id: 17)
4番目のcommentに紐づいているuserを1人取得(id: 16)
5番目のcommentに紐づいているuserを1人取得(id: 16)
6番目のcommentに紐づいているuserを1人取得(id: 16)

を表している。

このように、post_id: 8にされたコメントの数(6件)+Commentを全件取得の1回で、合計7回SQLが発行されてしまう(usersテーブルに対してcommentの数(6回) + commentsテーブルに対して全件取得(1回)) 。
これがN+1問題と呼ばれている。
このままでは、もしpost_id: 8にされたコメントの数が10000件になれば10001回SQLが発行されることになってしまい、アプリのパフォーマンスの低下につながる。

解決方法

post_id: 8Commentを全件取得し、その段階でそれに紐づいているuserを取得すれば、テーブルを参照する回数は2回で済み、コメントが何件になろうともSQLの発行回数は2回で済む。

具体的には、allメソッドincludesメソッドに変えるだけである。

posts_controller.rb
def show
    @post = Post.find(params[:id])
    @comments = @post.comments.includes.order(created_at: :desc)
end

includesメソッドとは、関連している複数のテーブルからデータを取得してくるときのアクセス回数を大きく減らすことができるメソッド。また、事前に検索やフィルタリング、ソートなどをしたデータを取得することもできるため、アプリケーション側でそれらの処理を行う必要がなくなる

※ 書き方は、モデル名.includes(:関連名)
includesメソッドに渡す引数は、テーブル名ではなくアソシエーションで定義した関連名を指定する(テーブル名でないから注意)


includesメソッドを使った場合に発行されるSQL
Image from Gyazo

※ オレンジ色の線で、post_id: 8Commentを全件取得し、赤色の線でcommentに紐付くuserのデータを取得している。これなら、コメントが10000件になってもSQLの発行回数は2回で済む。


(参考:Railsガイド

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?