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?

はじめに

Webアプリケーションを開発していると

「動作は正しいのにデータ量が増えると急に遅くなる…」

という状況に遭遇することがあります。

その原因としてよく挙げられるのが 「N+1問題」 です。

これはPHP/Java/Ruby/Python/Go/Node.jsなど言語を問わずあらゆるアプリケーションで発生しうる問題です。

N+1問題とは?

たとえば、データベースに保存しているユーザー一覧と、そのユーザーに紐づく住所情報を一緒に扱いたいとします。

このとき、次のようなコードを書いてしまうと
ユーザー1件ごとに追加でSQLが発行されてしまうため、ユーザー数に比例してクエリ回数が増えていきます。

ユーザーが 10件なら 11回、1,000件なら 1,001回……というように
最終的にはパフォーマンスの低下につながります。

これが、いわゆる 「N+1問題」 と呼ばれるものです。

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        // すべてのユーザーを取得
        $users = User::all();
        $usersData = [];

        foreach ($users as $user) {
            $usersData[] = [
                'id' => $user->id,
                'name' => $user->name,
                // Model側に address のリレーションを定義している想定
                'address' => $user->address?->full_address,
            ];
        }

        return $usersData;
    }
}

上記のコードは、バックエンド側で起こり得るケースとしてPHP/laravelでシンプルに書いたものです。
動作としては正しいのですが、パフォーマンスの面ではあまり評価できません。

じゃあ、どうすべき?

とはいえ、関連テーブルのデータも一緒に扱いたい場面は多々あります。
その場合は、laravelであれば関連データを事前にまとめて取得する(Eager Loading) が定石です。

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{
    public function index()
    {
        // addressを事前にまとめて取得(Eager Loading)
        $users = User::with('address')->get();

        $usersData = [];

        foreach ($users as $user) {
            $usersData[] = [
                'id'      => $user->id,
                'name'    => $user->name,
                // Eagerロード済みなのでここで追加クエリは発生しない
                'address' => $user->address?->full_address,
            ];
        }

        return $usersData;
    }
}

これならユーザー数が何件に増えても、クエリ回数は「ほぼ2回」のままで済みます。

上記のコードは、「必要なカラムのみに絞る」などの工夫で
もう少しパフォーマンス改善できますが
N+1問題の改善方法としての大枠はこれで充分伝わると思います。

つまり、必要な関連データは事前取得しておき
可能な限りループの中で追加クエリを発生させないこと。

言語やフレームワークにより方法や書き方は違えど、根本的な考え方は変わりません。

開発をする際にこの点を意識して進めるだけでも、N+1問題による事故を防げるのではないでしょうか。

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?