0
3

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問題 (Bullet で検出し includes して対策)

Posted at

はじめに

未経験からWeb系エンジニアへの転職を目指し、ポートフォリオ(投稿アプリ)を作成中です。
今回は、ポートフォリオにN+1問題を対応させてみたので、まとめてみました!

前提

  • Ruby2.7.0
  • Ruby on Rails6.0.3
  • MySQL8.0

N+1問題とは

  • 必要以上にSQLの処理が行われて、システムのパフォーマンスが低下してしまうこと。
  • レスポンスが遅く、ユーザビリティの低いサービスの原因の多くは、SQL周りにあることが多いらしい。
  • SQLの発行回数を減らし、パフォーマンスを上げることが大事。

N+1問題の検出

まずは、N+1問題を検出するためBulletというgemを試してみる。

Gemfile
group :development, :test do
  gem 'bullet'
end
ターミナル
 bundle

Bulletは config/environments/development.rbに設定を追加しないと動作しないのでファイルを編集する。

config/environments/development.rb
Rails.application.configure do
  config.after_initialize do
    Bullet.enable = true # Bulletを有効化
    Bullet.alert = true # JavaScriptのポップアップアラートを有効化
    Bullet.bullet_logger = true # Rails.root/log/bullet.logに出力
    Bullet.console = true # ブラウザのconsole.logに出力
    Bullet.rails_logger = true # Railsのログに結果を出力
    Bullet.add_footer = true # ページの左下に結果を表示
  end
end

いざ、確認!

ローカル環境で投稿一覧画面にアクセスしてみると、N+1問題が検出され、この問題を解決するためには .includes([:user])を追加すればよいとある。

ターミナル
user: hako
GET /posts/index
USE eager loading detected
  Post => [:user]
  Add to your query: .includes([:user])
Call stack
  /Users/hako/portfolio/cafe_app/app/views/posts/index.html.erb:48:in `block in _app_views_posts_index_html_erb___2990704399900080110_90940'
  /Users/hako/portfolio/cafe_app/app/views/posts/index.html.erb:5:in `_app_views_posts_index_html_erb___2990704399900080110_90940'

N+1問題の対策

そこで、 app/controllers/posts_controller.rb .includes([:user])を追加してみる。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    # @posts = Post.page(params[:page]).per(6).order(:created_at => :desc) # 編集前
    @posts = Post.includes([:user]).page(params[:page]).per(6).order(:created_at => :desc) # 編集後
  end
end

再アクセスしてみるとBulletによる警告が表示されなくなった!
今度は投稿へのコメントにアクセスしてみると、以下のメッセージが表示されたので、

ターミナル
user: hako
GET /posts/48
USE eager loading detected
  Comment => [:user]
  Add to your query: .includes([:user])
Call stack
  /Users/hako/portfolio/cafe_app/app/views/comments/_comment.html.erb:3:in `block in _app_views_comments__comment_html_erb__2537338689005093815_102800'
  /Users/hako/portfolio/cafe_app/app/views/comments/_comment.html.erb:2:in `_app_views_comments__comment_html_erb__2537338689005093815_102800'
  /Users/hako/portfolio/cafe_app/app/views/posts/show.html.erb:65:in `_app_views_posts_show_html_erb__3096142757258343957_102780'

同様に、 .includes([:user])を追加。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def show
    @user = @post.user
    # @comments = @post.comments # 編集前
    @comments = @post.comments.includes([:user]) # 編集後
    @comment = @post.comments.build
  end
end

N+1問題が検出されなくなるまで、メッセージが表示される箇所に一つずつincludesを追加。
なお、Bulletを使っている中でN+1の警告を無視したい場合は、ホワイトリスト形式で config/initializer/bullet.rbに登録しておけば良いらしい。
今後いろいろ試してみたい。

終わりに

以上の対策をした後、本番環境にアクセスしてみるとパフォーマンスが上がってびっくり!動作が早い早い!!
ユーザー側に立つと、パフォーマンスが遅いとそれだけでストレスだし、そのサービスを使いたいと思えないもの。
今後は、SQLの実行回数をいかに減らせるかを意識してコードを書いていこうと思う!
引き続き、SQL周りの知識も深めていきたい。

初学者のため、理解不足な点が多々あると思います。気になった点やアドバイスなどありましたら、教えていただけますと幸いです!

参考サイト

ポートフォリオのバージョンアップ2020 7/2 (N + 1 問題)

Railsメモ(19) : BulletでN+1問題を検出する

N+1問題を発見しDBのクエリを改善するBullet

0
3
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
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?