🐕
【Laravel】EagerロードでN+1問題を解決する
はじめに
LaravelではでN+1問題を解決するためにEagerロード
が準備されています。
今回、公式ドキュメントを参考に、Eagerロード
の使用の有無でクエリの実行件数とかかった時間を比較してみました。
なお、books, authorsテーブルにはそれぞれ100件のテストデータを入れています。
準備
Bookモデルにリレーションを追記します。
app/Models/Book.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
public function author()
{
return $this->belongsTo(Author::class); // 追記
}
}
検証
Eagerロードを使わない場合と使った場合の2パターンを比較します。
クエリの内容、実行時間の確認にはenableQueryLog
を使用しました。
Eagerロードを使わない場合
まずは全ての本を取得して、その後本の著者を取得するために各本に対して別のクエリを実行してみます。
app/Http/Controllers/BookController.php
<?php
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
/**
* Handle the incoming request.
*
* @return \Illuminate\Http\Response
*/
public function __invoke()
{
\DB::enableQueryLog();
$books = Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
dd(\DB::getQueryLog());
}
}
実行したところ、計101件のクエリが実行されていました。
1回は本のデータ取得、そのほか100件は各本の著者を取得するクエリです。
実行時間はまちまちですが、約108ミリ秒でした。
# 一回目
select * from `books`
# 二回目以降
select * from `authors` where `authors`.`id` = 1 limit 1
select * from `authors` where `authors`.`id` = 2 limit 1
select * from `authors` where `authors`.`id` = 3 limit 1
・
・
・
Eagerロードを使った場合
次にEagerロード
を使用してみます。
app/Http/Controllers/BookController.php
<?php
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
/**
* Handle the incoming request.
*
* @return \Illuminate\Http\Response
*/
public function __invoke()
{
\DB::enableQueryLog();
$books = Book::with('author')->get(); // 変更
foreach ($books as $book) {
echo $book->author->name;
}
dd(\DB::getQueryLog());
}
}
実行したところ、計2件のクエリが実行されていました。
1回は本のデータ取得、もう1回は全ての本の全ての著者を取得するクエリです。
where in
を使うことで一括で著者を取得していることが分かります。
ちなみに著者を取得するクエリはforeach
の中ではなく、Book::with('author')->get()
の時点で実行されています。
実行時間は約9ミリ秒でEagerロードを使用しない場合(108ミリ秒)と比較して実行速度が上がっておりパフォーマンスが向上しています。
# 一回目
select * from `books`
# 二回目
select * from `authors` where `authors`.`id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ... 100)
Discussion
Eagerロード初心者の僕にはシンプルで分かりやすいと感じる記事でした😌