はじめに
未経験からWeb系エンジニアへの転職を目指し、ポートフォリオ(投稿アプリ)を作成中です。
今回は、ポートフォリオにN+1問題を対応させてみたので、まとめてみました!
前提
- Ruby2.7.0
- Ruby on Rails6.0.3
- MySQL8.0
N+1問題とは
- 必要以上にSQLの処理が行われて、システムのパフォーマンスが低下してしまうこと。
- レスポンスが遅く、ユーザビリティの低いサービスの原因の多くは、SQL周りにあることが多いらしい。
- SQLの発行回数を減らし、パフォーマンスを上げることが大事。
N+1問題の検出
まずは、N+1問題を検出するためBulletというgemを試してみる。
group :development, :test do
gem 'bullet'
end
$ bundle
Bulletは 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])
を追加してみる。
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])
を追加。
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 問題)