サトウのブログ

IT関連のことを書いていきます

N+1問題

最近「Ruby on Rails 5アプリケーションプログラミング」という本でRailsを勉強しています。

Railsチュートリアルを終えて、アプリ開発を進めてみたものの、知識の継ぎ接ぎ感が一向に拭えず、、、
アプリ開発が一区切りついたので、体系的にRailsを学びたいと思いこの本を読み始めました。

前置きはそれぐらいにして、その本の中でビューヘルパーの「grouped_collection_select」が出てきました。
その節では多対多の関係にある「books」テーブルと「authors」テーブルを使って、
各authorごとにグルーピングして本を選択できるようにするという感じです。f:id:koji-sato1242:20190525165044p:plain
controllerとviewは以下の通り。

def group_select
  @review = Review.new
  @authors = Author.all
end
<%= form_for(@review) do |f| %>
    レビュー対象
    <%= f.grouped_collection_select :book_id, @authors, :books, :name, :id, :title %>
<% end %>

これを動かしたときのターミナルはこんな感じ。
f:id:koji-sato1242:20190525165842p:plain

SQL発行されすぎじゃない?
author増えていったらDB死ぬやつですね。


ググってみるとこの現象が「N+1問題」というやつみたいです。
N+1問題 / Eager Loading とは - Rails Webook


余談ですが、最初「N+1問題」という言葉を見たとき、
『N+1問題』ってクライアントが増えていったときのHTTPサーバのパフォーマンスの問題じゃなかったっけ
と思いましたが、それは「C10K問題」でした。いっぱい問題がありますね。


「N+1問題」の解決方法としては、「includes」メソッドを使って、事前にauthorと関連するbookを取っておくという方法です。
ループの中で毎回bookを取り行くのではなく、ループに入る前にいっきに取っておくといった感じです。

includesを追加してみました。

def group_select
  @review = Review.new
  @authors = Author.all.includes(:books)
end

このときのターミナルはこんな感じ。
f:id:koji-sato1242:20190525172801p:plain

INを使ってSQLが1つにまとめられてます。解決です。

O/Rマッパーは便利な分、こういった問題があるので気をつけないといけないですね。