Back to blog

Advanced Laravel Query Optimization: From N+1 to Perfection

Laravel Performance Database Optimization

One of the most common performance issues in Laravel applications is the N+1 query problem. It's easy to create, harder to catch, and devastating for performance when you have large datasets.

Understanding N+1 Queries

The N+1 problem occurs when you execute one query to get a collection of records, then execute N additional queries to fetch related data for each record.

// BAD: Causes N+1 queries
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name; // This triggers a query for EACH post
}
// Result: 1 query + N queries = N+1 total queries

The Solution: Eager Loading

Laravel provides elegant solutions to eliminate N+1 queries:

// GOOD: Uses 2 queries total
$posts = Post::with('author')->get();
foreach ($posts as $post) {
    echo $post->author->name; // No additional queries!
}

Going Deeper: Multiple Relations

Most applications have complex relationship hierarchies. Livewire makes it even more critical to optimize:

$posts = Post::with([
    'author',
    'comments' => fn($query) => $query->latest()->limit(5),
    'comments.author',
    'tags',
])->paginate(15);

This single query loads posts with their authors, the 5 most recent comments with their authors, and tags—all efficiently.

Lazy Eager Loading

Sometimes you need to load relations conditionally. Laravel 8+ introduced loadMissing():

$post = Post::first();

// Load comments only if not already loaded
$post->loadMissing('comments');

Debugging with Telescope

Use Laravel Telescope in development to visualize your queries. You'll quickly spot N+1 issues and other bottlenecks.

Memory vs Database Trade-offs

Sometimes you need to be clever about what you load. Use select() to only fetch necessary columns:

$posts = Post::select('id', 'title', 'slug', 'author_id')
    ->with(['author' => fn($q) => $q->select('id', 'name')])
    ->get();

This reduces memory footprint and database load significantly.

Advanced: Relationship Count Aggregates

Laravel 8+ allows you to add counts without additional queries:

$posts = Post::withCount('comments')
    ->withCount(['views' => fn($q) => $q->where('created_at', '>', now()->subMonth())])
    ->get();

// Access via $post->comments_count

Monitoring in Production

Use tools like Blackfire.io or New Relic to monitor query performance in production. Real user data will reveal optimization opportunities you never considered.

Conclusion

Query optimization is an ongoing process. Always think about relationships upfront, use Telescope in development, and monitor in production. Your users will appreciate the speed, and your database will thank you for the efficiency.

You Might Also Like