fluid_27’s blog

勉強した内容をアウトプットするためのブログ

N+1問題が発生していたので、解決してみた

ハッカソンで共同開発しているアプリで、いわゆる「N+1 問題」が発生していた。

ログを見ているとめちゃめちゃDBにアクセスしている。。

 

 

実際にN+1問題がでていたコード

Posts.controller.rb

  @posts = Post.all

 

index.html.erb

<% @posts.each do |post| %>

  <ul>

    <li>ポストID:<%= post.id%> </li>

    <li>投稿者:<%= link_to "#{post.user.name}", show_user_path(post.user)%></li>

    <li>日付:<%= post.date%>曜日:<%= post.day_of_week %></li>

    <li>時刻:<%= post.time %>時</li>

    <li>方面:<%= post.direction%></li>

    <li>駅:<%= post.station.station_name%></li>

    <li>混雑具合:<%= post.congestion_level%></li>

    <li>コメント:<%= post.comment%></li>

    <%= button_to '投稿削除', delete_post_path(post.id), data: {confirm: '本当に投稿を削除しますか?'}, method: :delete, class: "btn btn-warning" %>

  </ul>

<% end %>

 

UserとPostとStationテーブルがあり、関係は

 

 User.rb

  has_many :posts

 

Post.rb

  belongs_to :user

  belongs_to :station

 

となっている。

 

 

原因

コントローラーで Post.all である @posts をviewに渡して、それをeach文で回しながら、それぞれ関連づいている

  • post.user
  • post.station

などを表示させる仕組みだったが、それらの関連づいたデータを表示させる際に、一回一回DBにデータをとりに行っていた為に、DBへのアクセス回数が増え、いわゆる N+1問題が発生していた。

 

解決法

ググったらすぐに出てきた。

controllerで

Post.allでなく

Post.all.includes(:user, :station)

とするとPostに関連づいた User, Postも取得してくれて post.user などを表示させる際に一々DBにアクセスしなくなる。 

 

上記のincludes(:user, :station)は

関連づいているモデルとの関係が 1:N なのか 1: N : 1なのか、とかで書き方変わるみたい。

詳しくは下記サイトにありました。

https://qiita.com/hirotakasasaki/items/e0be0b3fd7b0eb350327

 

 

countでも発生していた

思ったよりも簡単なコード記述で解決できるんだな。

と思っていたらログを見ると、まだちょっとアクセスする回数が多い。。

よくよく見ると、ユーザーの投稿回数を表示させる

user.post.count

でやっぱり都度、DBにアクセスしている。

これもググったらすぐに出てきた。

結論からいうと、

user.post.count

 とすればいいだけだった。

 

参考サイト

https://qiita.com/awakia/items/60467b95f92bee9a469a

 

プログラミング初心者の自分にとって、「N+1問題っていうのがあるみたいだけど、そのうち調べてみよ」くらいに思っていたが、調べてみると、割と簡単な記述で回避できるんだなぁ。と思った。

includes以外にもN+1問題を解消する方法があるみたいなので、また更新しようと思います。