0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【5分で実践】「N+1問題?」「何をすれば?」「チーム貢献したい」人へ!【2ステップ始め方】

Posted at

こんな経験はありませんか?

  • 「N+1問題」という言葉をよく聞くけど、なんだか難しそう…
  • 調べても「Eager Loading」とか「遅延ロード」とか、専門用語が多くてピンとこない。
  • 解説記事はいっぱいあるけど、結局「自分は今、何をすればいいの?」と迷ってしまう。
  • 「パフォーマンスを意識しろ」と言われても、どこから手をつければ…

分かります。私もそうでした。「すごい技術」を学ぶ前に、もっと「現場ですぐに役立つこと」「チームに貢献できること」を知りたいですよね。

この記事は、そんなあなたのために書きました。

難しい理論や高度なテクニックは一旦置いておき、「N+1問題の『始め方』」に徹底的にフォーカスします。
技術力に自信がなくても大丈夫。この記事を読み終えた5分後には、「あ、自分もN+1問題を見つけられるかも!」と実践できる状態を目指します。

一緒に、現場で「おっ」と言われるエンジニアへの第一歩を踏み出しましょう!


📖 目次

  1. N+1問題って、結局なに?(30秒でわかる「幹事」の例え)
  2. なぜ今、N+1問題に取り組むべきなの?(技術力より大切なこと)
  3. 【最重要】まずは「見つける」ことから!5分で実践できる最強ツール
  4. 見つけたらどうする?解消の「基本のキ」(Lazy LoadingとEager Loading)
  5. チームで取り組む!N+1問題を「予防」する文化づくり
  6. 優先順位はどうする?(全部やらなくて大丈夫!)
  7. まとめ:今日からあなたが変わる第一歩

1. N+1問題って、結局なに?(30秒でわかる「幹事」の例え)

N+1問題とは、ものすごくざっくり言うと、「データベースへの無駄な問い合わせが、ループ処理のせいで大量発生しちゃう問題」のことです。

**「パーティーの幹事」**で例えてみましょう。

あなたはパーティーの幹事です。参加者10人(User)のリストを持っており、それぞれの「アレルギー情報(Profile)」を「名簿係(データベース)」に確認して、席札に書くことになりました。

ダメな例(N+1問題):

  1. まず、10人の参加者リスト(User)を1回の問い合わせで、名簿係(DB)から取得します。
  2. その後、参加者リストを一人ひとり順番に見て…
    • Aさんのアレルギー情報(Profile)を名簿係(DB)に聞きにいく(1回の問い合わせ)
    • Bさんのアレルギー情報(Profile)を名簿係(DB)に聞きにいく(1回の問い合わせ)
    • Cさんのアレルギー情報(Profile)を名簿係(DB)に聞きにいく(1回の問い合わせ)
    • ...
    • Jさんのアレルギー情報(Profile)を名簿係(DB)に聞きにいく(1回の問い合わせ)

これだと、参加者リスト取得(1回)+ 参加者10人分のアレルギー情報を個別に取得(10回)= 合計11回も名簿係(データベース)に問い合わせが発生してしまいます。

お客さんが100人いたら? 101回です。これが「N+1」問題と呼ばれるゆえんです。

イケてる例(N+1問題の解消):

  1. 名簿係(DB)に、「参加者10人のリスト」と「その10人全員のアレルギー情報」を、まとめて先に聞いておきます。(問い合わせはたった2回
  2. 手元にある2つのリストを組み合わせて、Aさんにはコレ、Bさんにはコレ、と席札に書いていきます。

どちらが効率的か、一目瞭然ですよね。
データベースへの問い合わせ(通信)は、プログラムが動く上で最も時間がかかる処理の一つです。これが100回、1000回と発生すると、ページの表示は致命的に遅くなります。


2. なぜ今、N+1問題に取り組むべきなの?(技術力より大切なこと)

「でも、開発中はデータも少ないし、別に遅くないけど?」
そう思うかもしれません。しかし、N+1問題を放置することには、技術的な問題よりもっと大きな「チーム」や「ビジネス」に関わる問題が潜んでいます。

🚀 組織貢献①:ユーザー体験(UX)を守るため

N+1問題が引き起こす「ページの表示遅延」は、ユーザー体験(UX)を著しく損ないます。読み込みが3秒遅れるだけで、多くのユーザーは離脱してしまうと言われています。
私たちが作っているサービスが使われなくなってしまっては、元も子もありません。N+1問題を意識することは、ユーザーへの「おもてなし」であり、ビジネスへの直接的な貢献です。

🤝 組織貢献②:未来の自分とチームメイトを救うため

「今は大丈夫」が一番危険です。サービスが成長し、データが1万件、10万件と増えたとき、N+1問題は牙を剥きます。
ある日突然、サイトが激重になり、原因調査に追われる…そんな未来を避けるためにも、小さいうちに摘んでおくことが大切です。
これは、未来の自分自身と、将来そのコードを触るかもしれないチームメイトへの「優しさ」であり、「負債を残さない」という立派な貢献です。

✨ 組織貢献③:コードレビューでの「信頼」を得るため

駆け出しエンジニアがコードレビューでN+1問題を指摘されたり、逆にN+1を意識したコードを書いたりできると、シニアエンジニアやリーダーは「おっ」と思います。
この人は、ただ動くコードを書くだけでなく、パフォーマンス(=ユーザーやDB)のことまで考えられる人だ
という信頼につながります。これは、難しいアルゴリズムを知っていることよりも、現場ではずっと価値が高いスキルです。


3. 【最重要】まずは「見つける」ことから!5分で実践できる最強ツール

N+1問題の解消法を暗記する前に、まずは「N+1問題がどこで起きているかを見つける」スキルを身につけましょう。これが「始め方」の第一歩です。

そして、そのための最強の相棒が「Laravel Debugbar」です。

🚀 5分で実践!Laravel Debugbarの導入

まだ導入していない方は、今すぐ導入しましょう!
(※本番環境には導入しないでくださいね。開発環境(ローカル)のみです。)

  1. インストール
    ターミナルで、あなたのLaravelプロジェクトのディレクトリに移動して、以下のコマンドを実行します。

    composer require barryvdh/laravel-debugbar --dev
    

    --dev は開発環境にのみインストールするおまじないです)

  2. 設定(ほぼ不要)
    最近のLaravelなら、これだけで使えるようになっているはずです。
    もし.envファイルに APP_DEBUG=false となっている場合は、APP_DEBUG=true に変更してください。(開発中は true が普通です)

  3. ブラウザで確認
    php artisan serve などで開発サーバーを起動し、ブラウザであなたの開発中ページ(例: http://localhost:8000)を開いてみてください。

    画面の下部に、黒いバーが出ていませんか? それがDebugbarです!

🔍 DebugbarでのN+1問題の見つけ方

使い方は簡単です。

  1. N+1が起きていそうなページ(例:一覧ページ)をブラウザで表示します。
  2. 画面下部のDebugbarにある「Queries」タブをクリックします。

ここがチェックポイントです!

「似たような SELECT 文が、大量に(ループの回数分)発行されていないか?」

例えば、select * from "profiles" where "profiles"."user_id" = ? のようなSQLが、user_id の値だけを変えて10個も20個も並んでいたら…
それ、N+1問題です!

「解消」は後回しでも構いません。まずは、自分が書いたコードがN+1を引き起こしていないか、DebugbarのQueriesタブで確認する癖をつけましょう。

たったこれだけです。これが「N+1問題解消の始め方」です。


4. 見つけたらどうする?解消の「基本のキ」(Lazy LoadingとEager Loading)

DebugbarでN+1問題を見つけたら、いよいよ解消です。
解消法はいくつかありますが、初学者のうちはまず、最も基本的で強力な「Eager Loading(事前読み込み)」だけを覚えればOKです。

具体的には、with() メソッドを使います。

Before:N+1が発生するコード(ダメな例)

例えば、ブログ記事の一覧(posts)と、各記事の著者名(user)を表示したい場合。

コントローラー (PostController.php):

// 1. まず記事を全件取得 (ここでSQL 1回)
$posts = Post::all(); 

// viewに渡す
return view('posts.index', ['posts' => $posts]);

ビュー (posts/index.blade.php):

@foreach ($posts as $post)
    <h2>{{ $post->title }}</h2>
    
    <p>著者: {{ $post->user->name }}</p> 
@endforeach

$posts を取得した時点では、関連する user の情報は取得されていません。
このように、関連データが必要になったタイミングで初めてDBに問い合わせる仕組みを「遅延ロード(Lazy Loading)」と呼びます。

これが @foreach の中で呼ばれると、 $post->user->name とアクセスするたびに、「この記事のユーザー(user_id=1)は誰だ?」「この記事のユーザー(user_id=2)は誰だ?」…と、ループの回数だけSQLが発行されてしまいます。これがN+1問題の正体です。

After:Eager Loadingで解消するコード(イケてる例)

解消は、コントローラーをたった1行変えるだけです。

コントローラー (PostController.php):

// 1. 記事を取得する「ついでに」、関連する「user」もまとめて取得!
$posts = Post::with('user')->get(); // all() を get() に変え、with() を挟む

// viewに渡す
return view('posts.index', ['posts' => $posts]);

ビュー (posts/index.blade.php):

@foreach ($posts as $post)
    <h2>{{ $post->title }}</h2>
    
    <p>著者: {{ $post->user->name }}</p> 
@endforeach

(ビュー側は何も変更なし!)

with('user') と書くだけで、Laravelは「遅延ロード」の逆、
Eager Loading(事前読み込み)」を行ってくれます。

  1. すべての記事(posts)を取得する。(SQL 1回)
  2. 1で取得した記事に必要な user_id を全部集めて、関連する著者(users)を一度にまとめて取得する。(SQL 1回)

これで、SQLの発行回数は、記事が100件あろうと1000件あろうと、たったの2回になります。
これがEager Loading(withメソッド)の力です。


5. チームで取り組む!N+1問題を「予防」する文化づくり

N+1問題を見つけて直せるようになったら、次のステップです。
それは、「N+1問題をそもそも発生させない」ための予防策であり、チームへの働きかけです。
これこそが、最大の組織貢献の一つです。

ステップ1:まず「自分」が習慣化する

何よりもまず、自分自身がN+1を意識することがスタートです。

  • 新しくコードを書いたら、必ずDebugbarのQueriesタブを見る。
  • @foreachforeach の中で、リレーション(->user->comments など)を呼んでいないか、指差し確認する。

これを自分の「当たり前」にしましょう。

ステップ2:コードレビューで「優しく」気づきを共有する

もしあなたがチームのコードレビューで、他の人のコードにN+1問題を見つけたら…
チャンスですが、注意が必要です。技術マウントになってはいけません

悪い指摘例 👎:
「ここ、N+1になってます。with 使ってください。パフォーマンス考えてください。」
(言われた側は、責められたように感じてしまいます)

良い提案例 👍:
「お疲れ様です! ここのループ処理、もしかしたらデータが増えたときにN+1問題になってしまうかもしれません 🤔」
「よかったら、コントローラー側で with('user') を使うと、SQLの発行回数を減らせてパフォーマンスが良くなりそうです!」
「DebugbarのQueriesタブで見ると、発行されてるSQLが確認できて便利ですよ〜!」

ポイントは、「指摘」ではなく「提案」と「情報共有」にすること。
「自分もこれでハマったんですが…」と、自分の失敗談を交えるのも良いでしょう。
相手を尊重し、「一緒にコードを良くしましょう」という姿勢が、チームの心理的安全性を高め、あなたの信頼を築きます。

ステップ3:チームの「当たり前」に昇格させる(予防策)

個人の努力には限界があります。チーム全体でN+1を防ぐ仕組み(文化)を作ることが理想です。

予防策①:テストでN+1(遅延ロード)を検知する

Laravelには、N+1の原因となる「遅延ロード」が起きたらテストを失敗させる、強力な機能が備わっています。

tests/TestCase.php (または tests/Feature/TestCase.php など、基底となるクラス)の setUp メソッドに、以下の一行を追加します。

use Illuminate\Database\Eloquent\Model;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    protected function setUp(): void
    {
        parent::setUp();

        // ↓ これを追加!
        Model::preventLazyLoading(! $this->app->isProduction()); 
    }
}

これを設定しておくと、開発環境やテスト実行時に遅延ロードが発生した瞬間に、例外(エラー)を吐いて処理が止まるようになります。

注意点:
いきなり既存のプロジェクトに導入すると、既存の遅延ロード(N+1)が多すぎて、ほぼ全てのテストが失敗する可能性があります。
まずは自分の開発環境(ローカル)だけで試してみたり、新しく作るプロジェクトで最初から導入したりするのが現実的です。
「チームにこんな機能があるみたいですよ」と情報共有するだけでも、大きな一歩です。

予防策②:小さな勉強会を開く

「5分だけお時間ください!」と言って、Debugbarの見方や with の使い方をチームメンバーにデモするのも非常に効果的です。
あなたが学んだことを共有することで、チーム全体のスキルアップにつながります。


6. 優先順位はどうする?(全部やらなくて大丈夫!)

Debugbarを導入すると、既存のコードにN+1問題が大量に見つかって、愕然とするかもしれません。
「うわ…全部直さないと…」
その必要はありません! 完璧主義は挫折のもとです。

駆け出しのうちは、以下の優先順位で取り組みましょう。

🥇 最優先:今、自分が新しく書くコード・修正するコード

何よりもまず、これから自分が生み出すコードでN+1を発生させないこと。これが一番重要です。

🥈 次点:ユーザーが頻繁に見る「重い」ページ

もし既存コードに手を入れる余裕があるなら、以下のページを優先します。

  • サイトのトップページ
  • 検索結果一覧ページ
  • ダッシュボード

など、ユーザーが頻繁にアクセスし、かつデータ量が多い(=N+1の影響がデカい)ページから手をつけるのが効率的です。

🥉 その次:その他(ついでに直す)

それ以外の古いコードは、「何かの機能修正でたまたま触ったついでに、with を追加しておくか」くらいの温度感で大丈夫です。
(これを「ボーイスカウト・ルール」と呼んだりします。来たときよりも美しく!)


7. まとめ:今日からあなたが変わる第一歩

N+1問題は、決して「高度な技術トピック」ではありません。
それは、**「ユーザーの待ち時間を減らしたい」「DBサーバーに優しくしたい」「未来のチームメイトに負債を残したくない」**という、**基本的な「思いやり」と「気づかい」**の問題です。

今日、あなたに覚えて帰ってほしいステップは、たった2つです。

  1. 今すぐ Laravel Debugbar を入れること。
  2. コードを書いたら、必ず「Queries」タブを見ること。

with の使い方を忘れても構いません。まずは「あ、なんか同じSQLがめっちゃ出てる!」と気づけることが、100倍重要です。

この小さな習慣が、あなたのコードの品質を劇的に改善し、チームからの信頼を勝ち取る大きな一歩となります。

さあ、今すぐ composer require を実行してみましょう!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?