はじめに
LazyLoading
とEager Loading
ってどれぐらい速度が変わるんだ?と思ったので比較してみます。
環境
M1 Mac
ProductName: macOS
ProductVersion: 11.6
BuildVersion: 20G165
Docker for Mac
Docker version 20.10.12, build e91ed57
PHP, Laravel
$ 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
$ php artisan --version
Laravel Framework 9.31.0
テーブル
長くなっちゃうのでだいぶ端折ります。
migration
, factory
, seeder
, model
をそれぞれそれっぽいところを書きます。
Migrations
// authorsテーブルのマイグレーション定義
public function up()
{
Schema::create('authors', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
// booksテーブルのマイグレーション定義
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->integer('author_id', false, true)->references('id')
->on('authors')->onUpdate('CASCADE')->onDelete('CASCADE');
$table->string('name');
});
}
Fakers
// AuthorFactory
public function definition()
{
return [
'name' => $this->faker->name()
];
}
// BookFactory
public function definition()
{
return [
'author_id' => Author::factory(),
'name' => $this->faker->sentence,
];
}
Seeders
// BookSeeder
public function run()
{
\App\Models\Book::factory(10000)->create();
}
// DatabaseSeeder
public function run()
{
$this->call([
BookSeeder::class,
]);
}
Model
// BookModel リレーション
public function author(): HasOne
{
return $this->hasOne(Author::class, 'id', 'author_id');
}
ターミナルでたーん!
$ php artisan migrate --seed
INFO Running migrations.
2022_09_28_054159_create_books_table ............................. 30ms DONE
2022_09_28_054234_create_author_table ............................ 27ms DONE
INFO Seeding database.
Database\Seeders\BookSeeder ........................................ RUNNING
Database\Seeders\BookSeeder ............................... 9,115.43 ms DONE
処理時間の計測
やっと準備完了しました。さっそく遊んで行きます。
Lazy Loading
// Lazy Loading
$time_start = microtime(true);
$books = Book::get();
foreach ($books as $book) {
$book->author;
}
$time_end = microtime(true);
$lazy_time = $time_end - $time_start;
echo ('Lazy Loading : ' . $eager_time . " [sec]\n");
unset($books, $time_start, $time_start);
// Eager Loading
$time_start = microtime(true);
$books = Book::with('author')->get();
$time_end = microtime(true);
$eager_time = $time_end - $time_start;
echo ('Eager Loading : ' . $eager_time . " [sec]\n");
echo ($lazy_time / $eager_time . '倍');
$ curl localhost:200/api
Lazy Loading : 5.0409960746765 [sec]
Eager Loading : 0.16714096069336 [sec]
30.16014778044 倍
30倍も早いんですね。
いろいろ変えて試してみる
カラム数とかって影響するのか気になったのでauthors
テーブルに1カラム追加します。
ALTER TABLE `authors` ADD COLUMN `random_string` VARCHAR(255) DEFAULT 'test' AFTER `name`;
誤差レベルですね・・・むしろ速くなってる。
$ curl localhost:200/api
Lazy Loading : 5.0331110954285 [sec]
Eager Loading : 0.16527915000916 [sec]
30.452184048319 倍
indexはってみたらどうかしら。
CREATE UNIQUE INDEX `index_id` ON `authors` (`id`);
CREATE UNIQUE INDEX `index_id` ON `books` (`id`);
誤差ですねぇ。
Lazy Loading : 4.9837791919708 [sec]
Eager Loading : 0.17623591423035 [sec]
28.279021411361 倍
本の著者が複数人でもいいじゃないということで、一対多
リレーションに変更してみます。
変更したファイルだけ書いてます。
Migrations
// booksテーブル
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
// $table->integer('author_id', false, true)->references('id')
// ->on('authors')->onUpdate('CASCADE')->onDelete('CASCADE');
$table->string('name');
});
}
// authorsテーブル
public function up()
{
Schema::create('authors', function (Blueprint $table) {
$table->id();
$table->integer('book_id', false, true)->references('id')
->on('books')->onUpdate('CASCADE')->onDelete('CASCADE');
$table->string('name');
});
}
public function authors(): HasMany
{
return $this->hasMany(Author::class, 'book_id', 'id');
}
public function run()
{
$faker = \Faker\Factory::create();
\App\Models\Book::factory(10000)->create()->each(function ($book) use ($faker) {
\App\Models\Author::factory(2)->create([
'book_id' => $book->id,
'name' => $faker->name(),
]);
});
}
たーん
$ php artisan migrate:refresh
INFO Rolling back migrations.
2022_09_28_054234_create_author_table .................................................................................................. 20ms DONE
2022_09_28_054159_create_books_table ................................................................................................... 10ms DONE
2019_12_14_000001_create_personal_access_tokens_table ................................................................................... 6ms DONE
INFO Running migrations.
2019_12_14_000001_create_personal_access_tokens_table ................................................................................. 105ms DONE
2022_09_28_054159_create_books_table ................................................................................................... 31ms DONE
2022_09_28_054234_create_author_table .................................................................................................. 30ms DONE
$ php artisan db:seed
INFO Seeding database.
Database\Seeders\BookSeeder .............................................................................................................. RUNNING
Database\Seeders\BookSeeder .................................................................................................... 13,367.11 ms DONE
さらにたーん
$ curl localhost:200/api/
Lazy Loading : 130.49195289612 [sec]
Eager Loading : 0.20387506484985 [sec]
640.05842495854 倍
....ふぁっ!? Lazy Loading
の方は約26倍
も経過時間が増えているのに対してEager Loading
は0.03秒
しか増えていらっしゃらないので640倍
も速度差がでてしまいました。圧倒的じゃないか我が軍は・・・(にわか)
検証?おわり
ちゃんとEager Loading
を使わないとえらい目に合うことがよく分かりました。
参考
LaravelのEager Loading
、 Lazy Laoding
について
Eager Loading
, LazyLoading
のSQL発行回数の確認方法