目的
現役Webエンジニアの知り合いに過去に書いた記事を自慢げに見せたところ、
N+1問題って知ってる?
と言われました。
クロノスタシスよろしく、「知らな~い」と答えたところ、下記記事を紹介して頂いたのでこちらを参考に修正しました。
早速修正だ!
そもそもN+1問題というのは、has_many
で紐づけしたテーブルを呼び出す際に、いちいち全てのデータを参照して引っ張ってくることだと理解しました。
これによってデータベースへのアクセスにかかる時間が長くなり、サーバに負荷がかかってしまいます。
モデルで主従関係を明らかにする
過去記事では触れていなかったモデルについて言及します。
今回、Genreモデルが主、Hogeモデルが従の関係になっているので、
has_many :hoges
belongs_to :genre
とします。
モデルの主従関係については過去に記事を書いていますので、こちらを参考にしてください。
余談ですが、has_many
はその要素が沢山あるので複数形、belongs_to
は1つの要素だけなので単数形で書くのが一般的のようです。
コントローラでincludes
メソッドを使用する
モデルの主従関係を元に、アクション内で呼び出す変数も修正します。
修正前
# アクション内で
@genres = Genre.all
修正後
# アクション内で
@genres = Genre.includes(:hoges)
これにより、ビューファイルでHogeモデルを呼び出したいときは
@genres
に対してhoges
というメソッドを使用できるようになりました。
ビューファイルの修正
以上を踏まえ、ビューファイルを修正していきます。
修正前
<% @genres.each do |genre| %>
<p><%= genre.name %></p>
<% hoges = Hoge.where(genre_id: genre.id) %> # ここでN+1が発生
<table>
<tr>
<td>id</td><td>content</td>
</tr>
<% hoges.each do |hoge| %>
<tr>
<td><%= hoge.id %></td>
<td><%= hoge.content %></td>
</tr>
<% end %>
</table>
<% end %>
修正後
<% @genres.each do |genre| %>
<p><%= genre.name %></p>
# hogesの定義を削除
<table>
<tr>
<td>id</td><td>content</td>
</tr>
<% genre.hoges.each do |hoge| %> # genreに紐づくhogesを呼び出す
<tr>
<td><%= hoge.id %></td>
<td><%= hoge.content %></td>
</tr>
<% end %>
</table>
<% end %>
具体的には、hogesを定義した一文の削除と、genreからの紐づけによるhogeの呼び出しをおこないました。
これにより、N+1問題は解消されました。
まとめ
今回は
原因のひとつとして、ビューファイルでdbにアクセスしようとしたことが挙げられます。
変数の定義やそれに伴うdbへのアクセスは、モデルファイルやコントローラファイルといった、上位階層でおこなうべきだと気付かされました。
また本内容を指摘して頂いた経験から、独学で開発を進める上で自分よりも経験値のある(世の中の98%のエンジニアがそう)方と会うことの大切さを身をもって知りました。
コミュニティって大事ですね!