カテゴリ: Rails 更新日: 2026/02/03

RailsのN+1問題とは?クエリ最適化とincludes・eager_load・Bulletを初心者向けに徹底解説【ログの見方と再現例】

N+1問題とは?ログの見方と再現例から学ぶ根本原因【保存版】
N+1問題とは?ログの見方と再現例から学ぶ根本原因【保存版】

先生と生徒の会話形式で理解しよう

生徒

「Railsでアプリを作っていたら、動きがすごく遅くなりました。原因が分からなくて…」

先生

「画面は普通に表示されているけれど、裏側で無駄なデータ取得が大量に起きている可能性があります。」

生徒

「無駄なデータ取得って、どういうことですか?」

先生

「Railsでとても有名な“N+1問題”が起きているかもしれません。順番に見ていきましょう。」

1. Railsとは?まずは超かんたんな前提知識

1. Railsとは?まずは超かんたんな前提知識
1. Railsとは?まずは超かんたんな前提知識

Rails(Ruby on Rails)は、Webアプリケーションを作るための道具です。Webアプリとは、ブラウザで使えるサービスのことで、ブログやSNS、予約サイトなどが代表例です。

Railsでは、MVCという考え方を使います。これは役割分担のルールです。

  • Model(モデル):データを扱う担当(データベースと話す)
  • View(ビュー):画面を作る担当(見た目)
  • Controller(コントローラ):全体の司令塔

今回のN+1問題は、主にModel(Active Record)が関係します。Active Recordとは、Rubyのコードでデータベースを操作できる仕組みのことです。

2. N+1問題とは?初心者向けにたとえ話で理解しよう

2. N+1問題とは?初心者向けにたとえ話で理解しよう
2. N+1問題とは?初心者向けにたとえ話で理解しよう

N+1問題とは、必要以上にデータベースへ問い合わせ(SQL)を送ってしまう問題です。

たとえば、先生が生徒名簿を配る場面を想像してください。

・最初に「クラスの生徒一覧」を取りに行く(1回)
・そのあと、生徒一人ひとりについて「この生徒の住所を教えて」と毎回聞きに行く(N回)

これで合計1 + N回の質問になります。これがN+1問題の名前の由来です。

Railsでも同じで、一覧を取得したあとに、関連データを1件ずつ取りに行くと、アプリがどんどん遅くなります。

3. N+1問題の再現例:Railsコードで見てみよう

3. N+1問題の再現例:Railsコードで見てみよう
3. N+1問題の再現例:Railsコードで見てみよう

ここでは「ユーザー」と「投稿」があるとします。1人のユーザーは、たくさんの投稿を持っています。


# controller
@users = User.all

<% @users.each do |user| %>
  <p><%= user.name %></p>
  <p><%= user.posts.count %></p>
<% end %>

一見、問題なさそうに見えます。しかし裏側では、ユーザー一覧を1回取得したあと、ユーザーごとに投稿を取りに行くSQLが発行されています。

これがN+1問題の典型例です。

4. Railsのログを見てN+1問題を発見する方法

4. Railsのログを見てN+1問題を発見する方法
4. Railsのログを見てN+1問題を発見する方法

Railsでは、ログという記録が自動で残ります。ログとは「裏側で何が起きたかの履歴」です。

ターミナルに同じようなSQLが何度も表示されていたら要注意です。


SELECT "users".* FROM "users"
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 2
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 3

このように、似たSQLが何回も出ていたら、N+1問題が起きています。

5. includesでN+1問題を解決する方法

5. includesでN+1問題を解決する方法
5. includesでN+1問題を解決する方法

includesは、関連データをまとめて先読みする命令です。


@users = User.includes(:posts)

これだけで、ユーザーと投稿をまとめて取得できます。結果として、SQLの回数が激減します。

初心者の方は、「あとで使いそうなデータを最初に一緒に取る」と覚えると分かりやすいです。

6. eager_loadとは?includesとの違いをやさしく説明

6. eager_loadとは?includesとの違いをやさしく説明
6. eager_loadとは?includesとの違いをやさしく説明

eager_loadもN+1対策の一つです。こちらは、必ずJOINを使って1回のSQLで取得します。


@users = User.eager_load(:posts)

JOINとは、複数の表を合体させて取ってくる方法です。

細かい違いは気にせず、includesで解決しないときの選択肢くらいの理解で大丈夫です。

7. Bulletとは?N+1問題を自動で教えてくれる便利ツール

7. Bulletとは?N+1問題を自動で教えてくれる便利ツール
7. Bulletとは?N+1問題を自動で教えてくれる便利ツール

Bulletは、N+1問題を見つけて教えてくれる便利なツールです。

初心者が自力でログを見て判断するのは大変なので、Bulletを使うと安心です。


gem 'bullet'

設定すると、画面やログで「ここN+1ですよ」と教えてくれます。

まるで先生が横で見てくれているような存在です。

8. N+1問題が起きる根本原因と考え方

8. N+1問題が起きる根本原因と考え方
8. N+1問題が起きる根本原因と考え方

N+1問題の原因は、とてもシンプルです。

「関連データを意識せずにコードを書いてしまうこと」です。

Railsは便利ですが、何も考えずに書くと裏側で大量のSQLが動きます。

「一覧表示+関連データ」を書いたら、N+1を疑うクセをつけることが大切です。

これが、Railsのクエリ最適化の第一歩になります。

カテゴリの一覧へ
新着記事
New1
Ruby
“すべてはオブジェクト”を体感!初心者向けRubyのオブジェクト指向入門【irbで学ぶ】
New2
Ruby
Rubyの標準入出力を完全ガイド!puts・print・pの違いとデバッグ活用法
New3
Ruby
Gemとは?RubyGemsとBundlerを初心者向けに完全解説!依存関係管理も図解でわかりやすく理解
New4
Ruby
Rubyの文字エンコーディング入門!UTF-8・マジックコメント・外部/内部エンコーディングを完全解説
人気記事
No.1
Java&Spring記事人気No1
Ruby
Rubyのreduceとinject入門!合計計算や集計を初心者向けに分かりやすく解説
No.2
Java&Spring記事人気No2
Ruby
Rubyの文字列エンコーディング完全ガイド!Encoding・force_encoding・encodeを初心者向け解説
No.3
Java&Spring記事人気No3
Ruby
Rubyの始め方ガイド:インストールから最初のHello Worldまで(Windows/Mac/Linux)
No.4
Java&Spring記事人気No4
データベース
PostgreSQLのWHERE句を徹底解説!初心者でもわかるSQLデータ抽出の基本
No.5
Java&Spring記事人気No5
Ruby
Rubyのfind/detect/find_indexを徹底解説!目的のデータを素早く探す方法
No.6
Java&Spring記事人気No6
Ruby
Rubyで比較演算子を完全解説!==・===・<=>・eql? の使い分け
No.7
Java&Spring記事人気No7
Ruby
Rubyのselect/reject/filterの使い方を完全解説!初心者向けの条件抽出レシピ
No.8
Java&Spring記事人気No8
データベース
PostgreSQLで順位付け!ROW_NUMBER関数の使い方を初心者向けに徹底解説