5 Laravel Database Optimizations That Actually Matter in Production

Laravel apps often feel fast at first, but slow down as real users and data grow. In most cases, the issue isn’t the framework but it’s inefficient database queries. This article covers a few practical optimizations that can make a noticeable difference in real-world performance.

Fix N+1 Queries (Eager Loading)

The N+1 query problem occurs when your application runs one query to fetch data and then executes additional queries inside a loop for related data. This usually happens when accessing relationships, like fetching a user for each post. It might not be noticeable with small datasets, but in production it can lead to hundreds of unnecessary queries, slowing down your application. Using eager loading with with() allows you to load all related data in a single query, drastically reducing the number of database calls and improving performance significantly.

Problem

$posts = Post::all();

foreach ($posts as $post) {
    echo $post->user->name;
}

Solution

$posts = Post::with('user')->get();

Add Proper Indexes

Database indexes play a crucial role in improving query performance, especially as your data grows. Without indexes, the database has to scan the entire table to find matching records, which becomes slow and inefficient for large datasets. By adding indexes to columns that are frequently used in WHERE, JOIN, or ORDER BY clauses, you allow the database to locate data much faster. However, it’s important to use indexes wisely, as too many indexes can slow down write operations like inserts and updates. A well-indexed database can significantly reduce query time and is one of the most impactful optimizations for production applications.

Example

SELECT * FROM users WHERE email = 'test@example.com';
$table->index('email');

Select Only What You Need

Fetching more data than necessary is a common cause of slow queries and high memory usage. Using SELECT * or loading entire models pulls all columns, including large text or blob fields, even if you only need a few. In Laravel, you can optimize this by selecting only the columns you actually need using select(). This reduces memory consumption, speeds up query execution, and makes API responses lighter, which is especially important for mobile clients or low-bandwidth environments. Being deliberate about the data you fetch helps your application stay fast and scalable.

Bad (If i need only user name)

User::all();

Good:

User::select('id', 'name')->get();

See how you can clone queries in laravel.

Use Caching for Heavy Queries

Repeatedly running the same complex queries can put unnecessary load on your database and slow down your application. Caching allows you to store the results of these queries temporarily, so subsequent requests can be served quickly without hitting the database every time. In Laravel, you can use the Cache::remember() method to cache query results for a set period. This is especially useful for dashboards, statistics, or any data that doesn’t change frequently. Implementing caching properly can drastically reduce database load and improve response times, making your app feel much faster to users.

Cache::remember('users', 60, function () {
    return User::all();
});

Caching can be mainly used for dashboard, stats and rarely changing data.

Chunk Large Data Processing

Loading and processing large datasets all at once can quickly exhaust your server’s memory and even crash your application. Laravel provides the chunk() method, which allows you to process records in smaller batches, keeping memory usage low and operations stable. This is especially useful for background jobs, migrations, exports, or any task that deals with thousands of records. By handling data in chunks instead of all at once, you ensure your application remains reliable and performant even when working with large amounts of data.

User::chunk(100, function ($users) {
    foreach ($users as $user) {
        // process
    }
});

Bonus

Use Bulk Operations

When updating or inserting multiple records, performing operations one by one can be slow and resource-intensive. Laravel allows you to perform bulk operations, which execute a single query for many records at once. For example, instead of updating each user individually:

// Inefficient: updates each user one by one
foreach ($users as $user) {
    $user->update(['is_active' => true]);
}

// efficient:
User::whereIn('id', $users->pluck('id'))->update(['is_active' => true]);

Separate Read and Write Databases

For high-traffic applications, a single database server can become a bottleneck because reads and writes compete for the same resources. Laravel supports read/write database separation, allowing you to direct all write operations (inserts, updates, deletes) to a master database and read operations (selects) to one or more replica databases.

Example configuration in config/database.php:

'mysql' => [
    'write' => [
        'host' => ['master-db-host'],
    ],
    'read' => [
        'host' => ['replica1-host', 'replica2-host'],
    ],
    'sticky' => true,
    'database' => 'your_db',
    'username' => 'db_user',
    'password' => 'db_password',
    'driver' => 'mysql',
],

sticky => true ensures that after a write, reads go to the master for consistency. Laravel automatically balances reads across replicas.

Conclusion

In production, slow database queries can quickly become a major performance bottleneck. Laravel provides tools like Telescope and Debugbar, and you can also use MySQL’s slow query log to identify problematic queries. By adding indexes, using eager loading, and selecting only the columns you need, you can optimize these queries. Detecting and fixing slow queries early ensures your application stays fast and responsive for users.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top