##N + 1問題とは
要約するとSQLが沢山発行して動作が重くなってしまう問題
詳しくは以下の記事をご確認ください私も参考にさせていただきました。
bulletとは
N + 1クエリを自動で検出するgemですうっかりN+1問題が起こってもすぐに見つけることができます。
ただbulletが検出できないようなN + 1クエリがあるケースがあるので頼りすぎるのもよくないです。↓以下参考記事
bulletを使う前
自作のアプリケーションでSQLを発行し確認してみたところ以下のようになった
Processing by QuestionsController#index as HTML
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 18], ["LIMIT", 1]]
Rendering questions/index.html.erb within layouts/application
Question Load (1.3ms) SELECT DISTINCT "questions".* FROM "questions" WHERE "questions"."best_answer_id" IS NULL ORDER BY "questions"."created_at" DESC LIMIT $1 OFFSET $2 [["LIMIT", 10], ["OFFSET", 0]]
↳ app/views/questions/index.html.erb:51
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 18], ["LIMIT", 1]]
↳ app/views/questions/index.html.erb:52
これが100行ぐらい続くのである。。
##bulletをインストール
Gemfileに以下を追加します。
Gemfile
group :development
gem 'bullet'
end
bundle install
を実行します。
$ bundle install
そしてSQLを発行しているページにいく(筆者の場合indexページ)と以下のように表示された。
表示の通りコントローラーにincludes[:user])
に書き換える
- @questions = @q.result(distinct: true)
+ @questions = @q.result.includes(:user)
こうするとポップアップが表示されなくなりSQLがどう発行されているか確認してみた。
Processing by QuestionsController#index as HTML
User Load (1.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 18], ["LIMIT", 1]]
Rendering questions/index.html.erb within layouts/application
Question Load (0.7ms) SELECT "questions".* FROM "questions" WHERE "questions"."best_answer_id" IS NULL ORDER BY "questions"."created_at" DESC LIMIT $1 OFFSET $2 [["LIMIT", 10], ["OFFSET", 0]]
↳ app/views/questions/index.html.erb:51
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2, $3, $4, $5, $6, $7, $8) [["id", 18], ["id", 17], ["id", 14], ["id", 19], ["id", 13], ["id", 9], ["id", 11], ["id", 3]]
↳ app/views/questions/index.html.erb:51
70行ぐらいに減っていた!
includes
を指定することで関連をまとめて取得し、最小限のクエリ回数で読み込みます。
以上です