Привет всем…!!
Здесь я расскажу, в чем разница между использованием нетерпеливой загрузки и отложенной загрузкой в ​​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