A visual overview of progressive optimization levels from eager loading through lazy collections, showing memory reduction from 120 MB to 6.7 MB.

Conquering the 7 Levels of Laravel Performance Optimization

Laravel performance optimization is critical for creating fast and efficient applications. As a Laravel developer, you start with "just making it work," but as your application grows, performance quickly becomes a priority. This playbook guides you through essential optimization techniques to reduce memory usage and improve processing time, moving from foundational fixes to advanced performance improvements. You'll begin with the basics, like eager loading, and advance to sophisticated lazy loading techniques that can cut memory costs by up to 90 percent. Optimizing a Laravel application often feels like scaling a mountain. Each level builds on the last, taking you from beginner fixes to expert-level enhancements that significantly boost speed and resource efficiency. By the end of this guide, you'll have the tools to transform your application from sluggish to supercharged, making it faster and more responsive for your users.

Measuring Your Performance Gains#

Before you start optimizing, establish how to measure progress. Use simple counters for data volume and PHP's built-in functions for memory and timing metrics. This approach lets you quantify the impact of each optimization level you apply.

measure-performance.php · php
$posts_count = $posts->count(); // No. Of Posts
$users_count = $users->count(); // No. Of Users
$time_taken = round(microtime(2) - LARAVEL_START, 2) . ' Second'; // Time Taken
$memory_used = round((memory_get_peak_usage() / 1024 / 1024), 2) . ' MB'; // Memory Used

Track these metrics consistently as you move through each level. You'll see concrete differences in how much memory your queries consume and how long they take to execute.

Level 1: Eager loading to crush the N+1 problem from the start#

When you first begin optimizing Laravel, the initial hurdle is often the N+1 query problem. N+1 problems occur when additional queries are generated for each item in a dataset, multiplying the database load. Eager loading resolves this by loading related models in a single query.

level-1-eager-loading.php · php
$posts = Post::with('user')->get();
20,000Posts

measured benchmark

1,000Users

measured benchmark

38.90 MBMemory Used

measured benchmark

0.15 SecondTime Taken

measured benchmark

Level 2: Selective loading means data you need, nothing more#

At Level 2, you start questioning whether you really need to load all the data. Is every column essential for your task? Instead of loading everything, select only the necessary columns. This simplifies your queries, reduces memory usage, and makes your app faster.

level-2-selective-loading.php · php
$posts = Post::with('user:id,name')->select('id', 'user_id', 'title')->get();
20,000Posts

measured benchmark

1,000Users

measured benchmark

33.20 MBMemory Used

measured benchmark

0.13 SecondTime Taken

measured benchmark

Level 3: Split query optimization with smart separation#

You're stepping up your game now. Eager loading isn't always the answer. Instead, you manually separate queries, fetching posts first and then retrieving users in a more controlled and efficient way. This approach gives you finer control over what gets loaded and when.

level-3-split-query.php · php
$posts = Post::select('id','user_id','title')->get();
$users = User::query()->whereIn('id', $posts->pluck('user_id')->unique())->pluck('name', 'id');
20,000Posts

measured benchmark

1,000Users

measured benchmark

24.71 MBMemory Used

measured benchmark

0.12 SecondTime Taken

measured benchmark

Level 4: Database filtering for advanced querying#

You're exploring new techniques and letting the database do more work. Applying distinct filtering directly in your queries speeds up data processing and reduces the load on Laravel while keeping memory usage in check.

level-4-database-filtering.php · php
$query = Post::query();
$posts = (clone $query)->select('id', 'user_id', 'title')->get();
$users = User::query()
        ->whereIn('id', (clone $query)->select('user_id')->distinct())
        ->get();
20,000Posts

measured benchmark

1,000Users

measured benchmark

24.71 MBMemory Used

measured benchmark

0.07 SecondTime Taken

measured benchmark

Level 5: Speed mode on, skip Eloquent with toBase()#

At Level 5, you take optimization further by using the toBase() method to bypass Laravel's Eloquent overhead. This approach retrieves raw data directly, reducing processing time and memory usage. It's ideal for efficiently handling large datasets where you don't need Eloquent's model features.

level-5-tobase.php · php
$query = Post::query();
$posts = (clone $query)->select('id', 'user_id', 'title')
        ->toBase()
        ->get();
$users = User::whereIn('id', (clone $query)
        ->select('user_id')->distinct())
        ->toBase()
        ->get();
20,000Posts

measured benchmark

1,000Users

measured benchmark

12.90 MBMemory Used

measured benchmark

0.04 SecondTime Taken

measured benchmark

Level 6: Load data in chunks. The power move#

Now you handle large datasets efficiently by using chunkById. This method breaks data into smaller, manageable chunks, preventing memory overload and ensuring smooth processing, even with massive amounts of data. You process batches rather than loading everything at once.

level-6-chunking.php · php
$query = Post::query();
$posts = collect();
(clone $query)->select('id', 'user_id', 'title')
     ->toBase()
     ->orderBy('posts.id')
     ->chunkById(10000, function ($collection) use (&$posts) {
         $posts->push(...$collection);
     }, 'id');

$users = User::whereIn('id', (clone $query)
            ->select('user_id')
            ->distinct())
            ->toBase()
            ->get();
20,000Posts

measured benchmark

1,000Users

measured benchmark

12.21 MBMemory Used

measured benchmark

0.03 SecondTime Taken

measured benchmark

Level 7: Lazy loading for minimal memory, maximum performance#

Level 7 focuses on mastering lazy collections, which efficiently handles large datasets by combining chunking with Laravel's lazy processing. It processes data in smaller chunks, minimizing memory usage while maintaining high performance. This is the ultimate optimization for massive datasets.

level-7-lazy-collections.php · php
$query = Post::query();
$posts = collect();
(clone $query)->select('id', 'user_id', 'title')
     ->toBase()
     ->orderBy('posts.id')
     ->chunkById(5000, function ($collection) use (&$posts) {
         $posts->push(...$collection);
     }, 'id');

$users = User::whereIn('id', (clone $query)
            ->select('user_id')
            ->distinct())
            ->toBase()
            ->get();
20,000Posts

measured benchmark

1,000Users

measured benchmark

6.70 MBMemory Used

measured benchmark

0.02 SecondTime Taken

measured benchmark

Your Application Is Now Supercharged#

You've mastered the seven levels of Laravel optimization, transforming your application into a high-performance powerhouse. By utilizing techniques such as eager loading, chunking, and lazy collections, you've significantly decreased memory usage and processing time. The result speaks for itself: reducing memory from 120 MB to just 6.7 MB is a 95 percent reduction that directly improves your users' experience.

Remember, enhancing your application is an ongoing journey. Continue refining your approach, experimenting with new strategies, and pushing the boundaries of efficiency. Each level you master gives you tools to handle bigger datasets and more complex queries without sacrificing speed or stability.

Ready to optimize your application at scale? Explore our web performance optimization strategies, and learn how ongoing maintenance and support keeps your Laravel application performing at peak efficiency.

Questions this post answers

What is the N+1 problem in Laravel?
The N+1 query problem occurs when additional queries are generated for each item in a dataset, multiplying database load. Eager loading resolves it by loading related models in a single query instead of executing one query per item.
How much memory can you save with these optimization techniques?
By progressing through all seven levels of Laravel optimization, you can reduce memory usage by up to 95 percent, from roughly 120 MB down to under 7 MB when handling 20,000 posts and 1,000 users.
When should you use toBase() in Laravel queries?
Use toBase() when you need to bypass Eloquent overhead for large datasets. It retrieves raw data directly from the database, reducing processing time and memory usage significantly compared to full Eloquent model hydration.

Keep reading