Laravel Database Performance: Finding and Fixing Slow Queries

The first time I noticed a Laravel application becoming slow, I initially looked at the PHP code. The routes were working and nothing obvious appeared to be broken, but some requests were taking much longer than they should.

After digging into the application, I found that the database was doing more work than necessary. Some queries were scanning too many rows, some columns were missing useful indexes, and a few pages were executing the same queries repeatedly.

That experience changed how I approach Laravel performance. Instead of guessing where the problem is, I now start by measuring the queries, finding the expensive ones, and then checking why the database is executing them that way.

Start by Finding the Slow Queries

Before changing a query, I want evidence that it is actually causing the slowdown. Laravel provides query logging that can help during development and troubleshooting.

DB::enableQueryLog();

$queries = DB::getQueryLog();

This gives me a list of SQL statements executed during that part of the application. It is useful for spotting unnecessary queries, repeated queries, and queries that retrieve far more data than the page needs.

I don’t leave query logging enabled everywhere in production. It is a troubleshooting tool, not something I would turn on indiscriminately on a busy application.

Use Laravel Debugging Tools

Laravel Debugbar is another useful development tool. It makes database activity easier to see while working on a page and can expose repeated queries and other performance patterns.

composer require barryvdh/laravel-debugbar — dev

The important part isn’t simply installing a debugging tool. The value comes from looking at what it shows and asking why the application is making those queries.

Look for the N+1 Query Problem

One problem I pay particular attention to in Laravel is the N+1 query problem. For example, retrieving a list of users and then loading each user’s orders inside a loop can result in one query for the users plus another query for every user’s orders.

With a small dataset, that may go unnoticed. With hundreds or thousands of records, the number of database queries can grow quickly.

$users = User::with(‘orders’)->get();

Eager loading can reduce this unnecessary database activity by loading related records more efficiently.

Don’t Retrieve More Data Than You Need

I have also learned to avoid retrieving entire records when an application only needs a few columns.

$users = User::select(‘id’, ‘name’)->get();

Being specific about the columns you need can reduce memory usage and the amount of data transferred between the database and application.

Use Pagination for Large Datasets

Loading thousands of records into one Laravel response is rarely a good idea. Pagination limits how much data the application processes and returns at one time.

$users = User::paginate(25);

For very large datasets and background processing, Laravel’s chunking and lazy collection features can also be useful.

Check Your Database Indexes

One of the biggest improvements I have seen from database optimization has come from proper indexing. If an application frequently searches by a column such as email, account number, status, or created_at, the database may benefit from an appropriate index.

$table->index(‘status’);

$table->unique(‘email’);

Indexes can make reads much faster, but I don’t add them blindly. They consume storage and can add work to INSERT and UPDATE operations. The right indexes depend on how the application actually queries the data.

Use EXPLAIN to Understand the Database

When I find a query that looks suspicious, I want to know how the database plans to execute it. That’s where EXPLAIN becomes useful.

EXPLAIN SELECT * FROM users WHERE email = ‘example@example.com’;

The exact output depends on the database engine, but EXPLAIN can show information about indexes, rows examined, and the execution strategy. If a query scans a large table when I expected an index to be used, that is a signal to investigate further.

Watch Out for Queries Inside Loops

Putting database operations inside application loops can quickly turn a small operation into hundreds of database requests. Whenever I see a database query inside a loop, I ask whether the data can be loaded once, eager loaded, grouped, or otherwise retrieved more efficiently.

Don’t Optimize Without Measuring

One lesson I learned the hard way is that performance optimization should be based on measurements rather than assumptions. A complicated query isn’t necessarily the slowest query, and adding an index doesn’t automatically make every operation faster.

I prefer to measure first, make one change, and then measure again. That gives me a much clearer picture of whether the optimization actually helped.

My Practical Laravel Performance Workflow

1. Reproduce the slow request.
2. Measure the request and database activity.
3. Identify expensive or repeated queries.
4. Check for N+1 queries.
5. Review the columns being selected.
6. Check pagination and dataset size.
7. Review database indexes.
8. Use EXPLAIN on suspicious SQL.
9. Make one optimization at a time.
10. Measure again after the change.

What I Learned

The biggest lesson for me is that Laravel performance problems aren’t always caused by Laravel itself. The framework may generate perfectly valid SQL, but the application can still be inefficient if it retrieves too much data, runs unnecessary queries, loads relationships inefficiently, or lacks suitable database indexes.

The database needs to be treated as part of the application’s architecture, not just a place where data is stored.

Conclusion

Slow Laravel applications can be frustrating, especially when everything appears to be working correctly. The best place to start is with measurement. Find out which queries are running, how often they run, how much data they retrieve, and how the database executes them.

Laravel’s query logging and debugging tools can help during development. Eager loading can reduce N+1 problems, pagination can control large datasets, and proper indexes can improve frequently used queries. When a query still looks suspicious, EXPLAIN can reveal what the database is actually doing.

The main lesson is simple: don’t guess about database performance. Measure it, investigate the evidence, make a focused change, and measure again.