
Привет всем…!!
Здесь я расскажу, в чем разница между использованием нетерпеливой загрузки и отложенной загрузкой в eloquent. Началось с проблемы, когда загрузка запросов в базу данных шла медленно, что поначалу не составляло и полсекунды, чем дольше после сбора данных тем больше запросов становилось медленнее. Это приводит к снижению производительности приложений, ухудшению пользовательского опыта вплоть до нарушения бизнес-процессов.
После проверки возникает довольно проблематичная ситуация: запрашиваемая таблица неоднократно повторяет запрос к своей таблице отношений, что приводит к частому обращению к базе данных для выполнения одного и того же запроса. Эту проблему обычно называют запросами N+1. Этот запрос N+1 не будет обнаружен, если количество пользователей нашего приложения все еще мало или данные, хранящиеся в базе данных, все еще малы. Однако когда наше приложение становится больше, его используют многие пользователи, а хранимые данные приложения достаточно велики, это может повлиять на производительность приложения, которая становится медленнее. Поэтому необходимо обратить внимание на то, чтобы привыкнуть к использованию нетерпеливой загрузки при запросе к базе данных.
Эта проблема характерна для начинающих разработчиков из-за удобства eloquent, поэтому они не обращают внимания на запросы N+1. Решением этой проблемы может стать использование нетерпеливой загрузки. Используя эту нетерпеливую загрузку, мы запрашиваем базу данных только один раз, и это может повысить производительность приложения. Пример реализации самой активной загрузки довольно прост: вам нужно всего лишь использовать функцию with(), за которой следует имя отношения таблицы.
Отложенная загрузка
// Controller
public function index()
{
$posts = Post::where('status_id', 1)
->where('release_date','<', Carbon::now())
->take(25)
->get();
return view('post.index', compact('posts'));
}
// View
....
<Table>
<tr>
<th>No</th>
<th>Title</th>
<th>Category</th>
<th>Author</th>
<th>Status</th>
</tr>
@foreach ($posts as $index => $post)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $post->title }}</td>
<td>{{ $post->category->name }}</td>
<td>{{ $post->author->name }}</td>
<td>{{ $post->status->name }}</td>
</tr>
@endforeach
</Table>
....
Жаркая загрузка
// Controller
public function index()
{
$posts = Post::with(['category', 'author', 'status'])
->where('status_id', 1)
->where('release_date','<', Carbon::now())
->take(25)
->get();
return view('post.index', compact('posts'));
}
// View
....
<Table>
<tr>
<th>No</th>
<th>Title</th>
<th>Category</th>
<th>Author</th>
<th>Status</th>
</tr>
@foreach ($posts as $index => $post)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $post->title }}</td>
<td>{{ $post->category->name }}</td>
<td>{{ $post->author->name }}</td>
<td>{{ $post->status->name }}</td>
</tr>
@endforeach
</Table>
....
Это выглядит довольно просто и легко, но это действительно влияет на производительность приложения. В этой статье я сравню производительность при использовании активной загрузки и отложенной загрузки с использованием фиктивных данных, сгенерированных с помощью Faker. Сначала я создам модели сообщений, категорий и пользователей, которые связаны друг с другом. затем создайте фабрику для генерации фиктивных данных.
CategoryFactory.php
public function definition(): array
{
return [
'name' => fake()->name(),
'slug' => fake()->slug(),
];
}
StatusFactory.php
public function definition(): array
{
return [
'name' => fake()->word(2),
'slug' => fake()->slug(),
];
}
UserFactory.php
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
PostFactory.php
public function definition(): array
{
$status = Status::inRandomOrder()->first();
$category = Category::inRandomOrder()->first();
$author = User::inRandomOrder()->first();
return [
'title' => fake()->text(5),
'slug' => fake()->slug(),
'release_date' => now(),
'body' => fake()->text(40),
'status_id' => $status->id,
'category_id' => $category->id,
'author_id' => $author->id,
];
}
DatabaseSeeder.php
public function run(): void
{
User::factory(20)->create();
Category::factory(10)->create();
Status::factory(3)->create();
Post::factory(600)->create();
}
После создания фабрики выполните заполнение с помощью ремесленной команды db:seed .
php artisan db:Seed
Затем в контроллере я создаю 2 маршрута: первый маршрут будет использовать отложенную загрузку, а второй — нетерпеливую загрузку. Внутри контроллера я также добавил логику для расчета времени загрузки страницы. вы можете увидеть код ниже.
web.php
Route::get('/lazy-load', [TestingController::class, 'lazyLoad']);
Route::get('/eager-load', [TestingController::class, 'eagerLoad']);
TestingController.php
private $limit = 100;
public function lazyLoad()
{
$startTime = microtime(true);
$posts = Post::where('release_date','<', Carbon::now())->take($this->limit)->get();
$view = view('post.index', compact('posts'))->render();
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
Log::debug('Lazy load : '.$executionTime.' second');
return $view;
}
public function eagerLoad()
{
$startTime = microtime(true);
$posts = Post::with(['category', 'author', 'status'])->where('release_date','<', Carbon::now())->take($this->limit)->get();
$view = view('post.index', compact('posts'))->render();
$endTime = microtime(true);
$executionTime = $endTime - $startTime;
Log::debug('Eager load : '.$executionTime.' second');
return $view;
}
index.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Laravel App</title>
</head>
<body>
<h1>Daftar Post</h1>
<Table>
<tr>
<th>No</th>
<th>Title</th>
<th>Category</th>
<th>Author</th>
<th>Status</th>
</tr>
@foreach ($posts as $index => $post)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $post->title }}</td>
<td>{{ $post->category->name }}</td>
<td>{{ $post->author->name }}</td>
<td>{{ $post->status->name }}</td>
</tr>
@endforeach
</Table>
</body>
</html>
Затем я запускаю приложение с ремесленной подачей. После этого я открыл в браузере следующие 2 URL-адреса: http://localhost:8000/lazy-load, http://localhost:8000/eager-load. Затем Laravel запишет журнал времени выполнения, чтобы отобразить две страницы, которые можно увидеть по адресу storage/logs/laravel.log. следующие результаты:
[2023-08-21 14:37:15] local.DEBUG: Lazy load : 0.32411909103394 second [2023-08-21 14:37:21] local.DEBUG: Eager load : 0.021986961364746 second
Из этих результатов видно, что нетерпеливая загрузка имеет время выполнения в 2 раза или даже быстрее, чем ленивая загрузка. Для дальнейшего доказательства я попытаюсь отобразить данные, которые раньше составляли всего 100 данных, теперь я меняю $limit на 200 и 500. Вот результаты:
200 данных:
[2023-08-21 14:42:18] local.DEBUG: Lazy load : 0.5706250667572 second [2023-08-21 14:42:22] local.DEBUG: Eager load : 0.031558036804199 second
500 данных:
[2023-08-21 14:43:11] local.DEBUG: Lazy load : 1.4767460823059 second [2023-08-21 14:43:19] local.DEBUG: Eager load : 0.064614057540894 second
Из приведенных выше результатов видно, что отложенная загрузка может отображать страницу с 200 данными за 5 миллисекунд и с 500 данными за 1,5 секунды. Эти результаты сильно различаются при использовании быстрой загрузки, которая может отображать страницу с 200 данными за 0,3 миллисекунды и с 500 данными за 0,6 миллисекунды.
Заключение
Активная загрузка очень помогает в оптимизации запросов. Из приведенных выше экспериментальных результатов видно, что нетерпеливая загрузка имеет гораздо более высокую производительность по сравнению с ленивой загрузкой. Поэтому необходимо привыкнуть к использованию нетерпеливой загрузки при запросе к базе данных. Это может повысить производительность приложения, что приведет к более быстрой загрузке приложений, что может улучшить взаимодействие с пользователями наших приложений.
Это все, чем я могу поделиться в этой статье. Надеюсь, эта статья окажется полезной для вас и для меня. Спасибо, что прочитали эту статью, увидимся в моей следующей статье.
Давайте соединяться : Linkedin