MySQL Query Optimization for Laravel Developers | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    MySQL Optimization for Laravel: Covering Indexes, EXPLAIN ANALYZE, and Query Profiling        On this page       1. [  Why Your Eloquent Queries Are Slower Than They Should Be ](#why-your-eloquent-queries-are-slower-than-they-should-be)
2. [  Reading EXPLAIN ANALYZE ](#reading-explain-analyze)
3. [  Covering Indexes: The Single Biggest Win ](#covering-indexes-the-single-biggest-win)
4. [  Profiling Slow Queries in Laravel ](#profiling-slow-queries-in-laravel)
5. [  Practical Checklist ](#practical-checklist)
6. [  Takeaways ](#takeaways)

  ![MySQL Optimization for Laravel: Covering Indexes, EXPLAIN ANALYZE, and Query Profiling](https://cdn.msaied.com/327/a63b6cb7e6fa83812746015ccb2ee7b7.png)

  #laravel   #mysql   #performance   #eloquent   #database  

 MySQL Optimization for Laravel: Covering Indexes, EXPLAIN ANALYZE, and Query Profiling 
========================================================================================

     30 Jun 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Your Eloquent Queries Are Slower Than They Should Be  ](#why-your-eloquent-queries-are-slower-than-they-should-be)
2. [  02   Reading EXPLAIN ANALYZE  ](#reading-explain-analyze)
3. [  03   Covering Indexes: The Single Biggest Win  ](#covering-indexes-the-single-biggest-win)
4. [  04   Profiling Slow Queries in Laravel  ](#profiling-slow-queries-in-laravel)
5. [  05   Practical Checklist  ](#practical-checklist)
6. [  06   Takeaways  ](#takeaways)

 Why Your Eloquent Queries Are Slower Than They Should Be
--------------------------------------------------------

Most Laravel performance problems trace back to one of three root causes: missing indexes, indexes that exist but are never used, or queries that fetch far more data than they need. The fix is rarely "add Redis" — it's understanding what MySQL is actually doing.

This article focuses on three concrete skills: reading `EXPLAIN ANALYZE` output, building covering indexes, and profiling slow queries in a Laravel context.

---

Reading EXPLAIN ANALYZE
-----------------------

MySQL 8.0+ supports `EXPLAIN ANALYZE`, which executes the query and returns real timing data alongside the estimated plan. Run it directly or via Laravel's query log:

```php
// Log the raw SQL, then run EXPLAIN ANALYZE in your DB client
$sql = User::where('status', 'active')
    ->where('created_at', '>=', now()->subDays(30))
    ->orderBy('created_at')
    ->toSql();

// Or use DB::select directly
$plan = DB::select('EXPLAIN ANALYZE ' . $sql, ['active', now()->subDays(30)]);

```

Key fields to watch:

- **type**: `ALL` means a full table scan. `ref` or `range` means an index is being used.
- **rows**: MySQL's estimate of rows examined. A high number relative to returned rows signals a poor index.
- **Extra**: `Using filesort` and `Using temporary` are red flags for ORDER BY and GROUP BY performance.
- **actual time**: In `EXPLAIN ANALYZE`, the `actual time=X..Y` values show real loop timing in milliseconds.

```sql
-> Index range scan on users using idx_status_created  (cost=120.5 rows=980)
   (actual time=0.412..3.201 rows=874 loops=1)

```

If `rows` is 50,000 but `actual rows` is 12, your index is working but the selectivity is poor — consider a more selective composite index.

---

Covering Indexes: The Single Biggest Win
----------------------------------------

A covering index includes every column the query needs, so MySQL never touches the actual table rows (no "heap fetch"). This is especially powerful for paginated list queries.

Consider a typical admin list:

```php
User::where('status', 'active')
    ->select('id', 'name', 'email', 'created_at')
    ->orderBy('created_at', 'desc')
    ->paginate(25);

```

A standard index on `status` forces MySQL to fetch the row for every match to retrieve `name`, `email`, and `created_at`. A covering index eliminates that:

```php
// In a migration
Schema::table('users', function (Blueprint $table) {
    $table->index(['status', 'created_at', 'name', 'email'], 'idx_users_covering_list');
});

```

Now `EXPLAIN` will show `Using index` in the Extra column — the query is satisfied entirely from the index B-tree.

**Rule of thumb**: put the equality columns first (`status`), then the range/sort column (`created_at`), then the projected columns.

---

Profiling Slow Queries in Laravel
---------------------------------

Enable the slow query log in MySQL (`long_query_time = 1`) and point `mysqldumpslow` at the log. For development, Laravel's built-in query listener is faster:

```php
// AppServiceProvider::boot()
if (app()->isLocal()) {
    DB::listen(function ($query) {
        if ($query->time > 100) { // ms
            logger()->warning('Slow query', [
                'sql'  => $query->sql,
                'time' => $query->time,
                'bindings' => $query->bindings,
            ]);
        }
    });
}

```

For production, use Laravel Telescope's query watcher or Pulse's slow query recorder — both surface the call stack so you can trace the Eloquent call site without grep.

---

Practical Checklist
-------------------

- **Always run `EXPLAIN ANALYZE`** before shipping a new query that touches large tables.
- **Composite index column order matters**: equality predicates first, range/sort last, projected columns after.
- **Covering indexes** eliminate heap fetches and are the highest-leverage optimization for read-heavy list endpoints.
- **`Using filesort` is not always fatal** — if the result set is small, MySQL sorts in memory quickly. It becomes a problem at scale.
- **`DB::listen`** in local/staging catches slow queries before they reach production.
- **Avoid `SELECT *`** in Eloquent — it prevents covering indexes from working and increases network payload.

---

Takeaways
---------

- `EXPLAIN ANALYZE` gives you real execution timing, not just estimates — use it on every non-trivial query.
- Covering indexes are the single most impactful optimization for paginated list queries in Laravel admin panels.
- Laravel's `DB::listen` and Telescope's query watcher are your first-line profiling tools before reaching for external APMs.
- Column order in composite indexes is not arbitrary — get it wrong and MySQL ignores the index entirely.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Fmysql-optimization-for-laravel-covering-indexes-explain-analyze-and-query-profiling&text=MySQL+Optimization+for+Laravel%3A+Covering+Indexes%2C+EXPLAIN+ANALYZE%2C+and+Query+Profiling) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Fmysql-optimization-for-laravel-covering-indexes-explain-analyze-and-query-profiling) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  When should I use a covering index versus a regular composite index?        Use a covering index when your query selects a small, fixed set of columns and runs frequently — such as paginated admin lists. If the SELECT columns change often or are numerous, a covering index becomes expensive to maintain and a regular composite index on the WHERE/ORDER BY columns is sufficient. 

      Q02  Does EXPLAIN ANALYZE actually execute the query and affect production data?        Yes — EXPLAIN ANALYZE runs the query for real to collect actual timing. For SELECT queries this is safe. Never run EXPLAIN ANALYZE on INSERT, UPDATE, or DELETE in production without wrapping it in a transaction you immediately roll back. 

      Q03  How do I find which Eloquent model method is generating a slow query in production?        Laravel Telescope captures the full stack trace alongside each query. In production without Telescope, add a DB::listen callback that logs slow queries with debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10) to pinpoint the call site. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Laravel 13: New Features, Helpers, and Practical Upgrade Notes](https://cdn.msaied.com/339/58c4fa6fe9b6d25a2dac17c621b6f4c6.png) laravel laravel-13 upgrade 

### Laravel 13: New Features, Helpers, and Practical Upgrade Notes

Laravel 13 ships with async-first defaults, a leaner bootstrapping layer, and several quality-of-life helpers....

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-13-new-features-helpers-and-practical-upgrade-notes) [ ![Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping](https://cdn.msaied.com/337/05b39d16d0f88a5fb94d0cf74049b88b.png) laravel laravel-12 upgrade 

### Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping

Laravel 12 ships with a leaner skeleton, first-class route file organisation, and a revised application bootst...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-12-structured-route-files-slim-skeletons-and-the-new-application-bootstrapping) [ ![Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning](https://cdn.msaied.com/336/89d518450335e8fcdaa5be882cf4dd3e.png) laravel api resources 

### Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning

Go beyond basic API resources. Learn how to implement sparse fieldsets, conditionally load relationships, and...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-api-resources-sparse-fieldsets-conditional-relationships-and-versioning) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
