【PHP/Laravel】Laravel パフォーマンス改善|N+1問題の原因と解決方法

PHP / Laravel

LaravelN+1問題の原因と解決方法 を教えてくれないかな?

今回は、「最初の1回のクエリ + N回の追加クエリ」が実行されてしまう N+1問題Laravel での原因と解決方法 について、ご紹介します。

Laravel環境構築がまだの場合 は、下記より Laravel環境構築 を行なってください。

N+1問題 とは?

N+1問題 とは、「最初の1回のクエリ + N回の追加クエリ」が実行されてしまう問題 のことです。

例えば、投稿(Post)が1000件 あり、各投稿のユーザー(User)を表示する 場合、
まず 投稿(Post)を取得するクエリが1回実行 され、その後、各投稿に紐づくユーザー(User)を取得するクエリが投稿の件数分だけ実行 されます。
つまり、投稿が1000件ある場合、合計で1001回のクエリが発行されてしまいます
これが N+1問題 です。

N+1問題 のイメージ図

投稿(Post)の件数分(例:1000件)だけユーザー(User)を取得するクエリが実行される ため、
投稿数が増えるほどクエリ数も増えてしまいアプリケーションのパフォーマンス低下の原因 になります。

N+1問題 の原因

LaravelORMであるEloquentのリレーション は、デフォルトで Lazy Loading(遅延ロード)になっています

ORM(Object-Relational Mapping)とは、データベースのデータをPHPのオブジェクトとして扱う仕組みのこと です。
Laravel では Eloquent を使うことで、SQLを書かなくてもオブジェクトとして直感的にデータベース操作ができます

Lazy Loading とは、関連データが必要になったタイミングで、その都度データベースから取得する仕組み のため、ループ内でリレーションを呼び出すと、データの件数分だけクエリが実行され、N+1問題が発生 してしまいます。

N+1問題 が発生する コード例 と実行される SQL

N+1問題 が発生する例 として、Postモデル(postsテーブル)Userモデル(usersテーブル) としているため、モデルやテーブル、ダミーデータなどまだ準備していない場合 は、下記を参考にご準備ください。

コード例

SQLクエリログ を確認のため、簡易的に「routes/web.php」に記述しています

実行されるSQL(投稿が1000件の場合)

投稿がN件 あれば、1 + N回SQLクエリが発行 されます
投稿が増えるとSQLも増え、アプリケーションのパフォーマンス低下につながる

実際のSQLクエリログ (1000 + 1 = 1001件)

N+1問題 の解決方法

コード例

Eager Loading(先読みロード)を使うと、with(‘user’) を付ける だけで、ループ内で追加クエリが発生しなくなります
SQLクエリログ を確認のため、簡易的に「routes/web.php」に記述しています

実行されるSQL(投稿が1000件の場合)

投稿がN件 でも、SQLクエリはたった2回 だけになる
ループ内でユーザーを1件ずつ取得する必要がなくSQLクエリ数は 1 + N → 2回 に削減されます

実際のSQLクエリログ (1 + 1 = 2件)

users をまとめて IN句で100件全てまとめて取得

N+1問題解決後のイメージ図

  1. posts取得時に、usersをまとめて取得
  2. ループ内で参照しても、SQLクエリ数は変わらない
  3. 投稿件数がN回に増えても、SQLは2回のまま変わらない

まとめ

今回は、Laravel でよく発生する N+1問題 の原因と解決方法 について紹介しましたが、いかがでしたでしょうか?

N+1問題 とは、「最初の1回のクエリ + N回の追加クエリ」が実行されてしまう問題 のことで、
ループ内でリレーションを呼ぶたびに追加クエリが実行される ため、データ件数が増えるほどSQLが増え、アプリケーションのパフォーマンス低下につながります

原因は、Laravel で使用されている Eloquentのリレーションがデフォルトで Lazy Loading(遅延ロード)になっていること です。
Lazy Loading では、関連データが必要になったタイミングで都度データベースにアクセスするため、N+1問題が発生してしまいます

解決方法は、Eager Loading(先読みロード)を使い、with() を活用する ことで、関連データをまとめて取得でき、SQLクエリ数を大幅に削減すること ができます。
これにより、投稿件数が増えてもクエリ数は変わらず、N+1問題を防ぐことが可能 です。

まずは、この N+1問題 と発生原因with() を使った解決方法を理解する ことで、Laravel アプリケーションのパフォーマンス改善に役立てる ことができます。
Laravel の知識をさらに深めて、より効率的な開発を目指しましょう。

公式サイト

Laravel 12.x Eloquent:リレーション

12.x Eloquent:リレーション Laravel
tachu × tachu

Laravel / React フルスタックエンジニア

Webエンジニア歴15年、フリーランス歴8年で、PHP / Laravel を中心に、
React.js / Vue.js を用いたフルスタックでのWeb開発をしています。

技術記事は Laravel / React / Web開発 を中心に書いています。

Webサービスの開発、既存システム改善、機能追加、技術相談、小規模な開発など、お気軽にご相談ください。

PHP / Laravel
シェアする

コメント

タイトルとURLをコピーしました