はじめに
この記事は「LaravelのEloquent便利だなー、リレーションの使い方を調べて見よう。ふむふむwith
を使って書いとけばとりあえずいいんやな。」と、なぜwith
を使えば良いかを対して理解せずに使っていたのでN + 1問題
というものがなんなのか調べてみてわかったことをまとめる記事です。
環境
$ php artisan --version
Laravel Framework 8.36.2
$ php --version
PHP 8.0.23 (cli) (built: Sep 13 2022 11:09:24) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.23, Copyright (c) Zend Technologies
N + 1問題ってそもそもなに?
「N件のデータを取得し、それぞれに紐付くデータを取得する際にクエリを発行する回数がN + 1
回になってしまうこと。」らしいです。
LaravelのようなORMがあるフレームワークを使用しているとSQLの発行回数等の意識が薄くなりがちだと思いますがパフォーマンスに大きく影響するのでEloquent等を使用する際は自身が発行しているSQLの内容をきちんと理解するのが大切ですね。(ブーメラン)
N + 1問題が起こっているコード1例
リレーションについて調べているとよく出てくるBook
とAuthor
を例にしてかいてみます。
N件のBook
を取得しそれぞれに紐付くAuthor
を取得してみます。
// use, リレーションメソッド等は省略
$books = Book::all();
foreach ($books as $book) {
$book->author;
}
はい、これN + 1問題
起こってますよね!?って言っても見えないので見えるようにしてみます。
DB::enableQueryLog();
$books = Book::get();
foreach ($books as $book) {
$book->author;
}
dd($books, DB::getQueryLog());
dd()
の第2引数に渡しているDB::getQueryLog()
で発行されたクエリを見ることができます。
Book
取得で1回、Bookの件数分(N件)のAuthor
を取得するのでN + 1
回取得していることが確認できたと思います。
(*ログも出さないなんて不親切な記事なんだ)
N + 1問題の解決
Laravelではwith
を使ってN + 1問題
が発生しないようにできます。
DB::enableQueryLog();
$books = Book::with(['author'])->get();
dd($books, DB::getQueryLog());
確認するとBook
とAuthor
それぞれに1回ずつクエリが発行されていることがわかりました。めでたしめでたし。
おわりに
これを書いたきっかけが株式会社 ゆめみ
様がwith
についてソースコードリーディング会をされていたのでこっそり視聴させていただきました。(まったりな感じのソースコードリーディング会のようなものは参加しやすいので非常にありがたいですね。)
コードはなんとなくぎりぎりある程度・・・追うことができたのですが、Eager Loading
やLazy Loading
と言った単語を良く分かっていなかったため理解しておこうと思い色々調べたので記事にしました。それでは。