Optimizing Laravel Query Performance with Eager Loading
Optimizing Laravel Query Performance with Eager Loading
One of the most common performance issues in Laravel applications is the N+1 query problem. In this tutorial, I’ll show you how to identify and fix this problem using eager loading.
What is the N+1 Query Problem?
The N+1 query problem occurs when your code executes 1 query to retrieve N records, and then N additional queries to retrieve related data for each record.
Example: The Problem
// This looks innocent but causes N+1 queries!
$users = User::all(); // 1 query
foreach ($users as $user) {
echo $user->posts->count(); // N queries (one per user)
}
If you have 100 users, this code executes 101 queries: 1 to get users + 100 to get each user’s posts.
Identifying N+1 Queries
Laravel provides the debugbar package to visualize queries:
composer require barryvdh/laravel-debugbar --dev
Look for repeated similar queries in the debugbar. That’s your N+1 problem!
Solution: Eager Loading
Use Laravel’s with() method to eager load relationships:
// Eager loading - only 2 queries!
$users = User::with('posts')->get(); // 2 queries total
foreach ($users as $user) {
echo $user->posts->count(); // No additional queries
}
Now we only execute 2 queries: 1 for users + 1 for all posts.
Real-World Performance Benchmark
I tested this on a production dataset with 1,000 users:
| Method | Queries | Time |
|---|---|---|
| Without Eager Loading | 1,001 | 2,450ms |
| With Eager Loading | 2 | 58ms |
Result: 97.6% faster! 🚀
Advanced: Nested Eager Loading
You can eager load nested relationships:
$users = User::with('posts.comments.author')->get();
This loads users → posts → comments → comment authors in just 4 queries!
Conditional Eager Loading
Load relationships only when needed:
$users = User::when($includeP osts, function ($query) {
$query->with('posts');
})->get();
Lazy Eager Loading
If you forgot to eager load initially:
$users = User::all();
// Later in code, realize you need posts
$users->load('posts');
Common Pitfalls to Avoid
1. Over-Eager Loading
Don’t load relationships you don’t use:
// BAD: Loading data you don't need
User::with('posts', 'comments', 'likes', 'followers')->get();
2. Loading Too Much Data
Use select() to limit columns:
// Only get necessary columns
User::with(['posts' => function ($query) {
$query->select('id', 'user_id', 'title');
}])->get();
3. Forgetting Count Queries
For counts, use withCount():
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count; // No additional queries!
}
Best Practices
- Always eager load when iterating over relationships
- Use Laravel Debugbar to monitor queries in development
- Profile production with tools like New Relic or Laravel Telescope
- Load only what you need - don’t over-fetch
- Use query caching for frequently accessed data
Conclusion
Eager loading is a simple technique that can dramatically improve your Laravel application’s performance. By being mindful of the N+1 problem and using with(), withCount(), and other eager loading methods, you can reduce database queries by 80-90% in many cases.
Remember: Always profile before and after to measure the actual impact!