N+1問題
最近「Ruby on Rails 5アプリケーションプログラミング」という本でRailsを勉強しています。
Railsチュートリアルを終えて、アプリ開発を進めてみたものの、知識の継ぎ接ぎ感が一向に拭えず、、、
アプリ開発が一区切りついたので、体系的にRailsを学びたいと思いこの本を読み始めました。
前置きはそれぐらいにして、その本の中でビューヘルパーの「grouped_collection_select」が出てきました。
その節では多対多の関係にある「books」テーブルと「authors」テーブルを使って、
各authorごとにグルーピングして本を選択できるようにするという感じです。
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 %>
これを動かしたときのターミナルはこんな感じ。
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
このときのターミナルはこんな感じ。
INを使ってSQLが1つにまとめられてます。解決です。
O/Rマッパーは便利な分、こういった問題があるので気をつけないといけないですね。