Laravel slow performance on pagination with large data

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Mastering Large Datasets: Fixing Slow Laravel Pagination with Joins and Random Ordering

As a senior developer working with large databases, we frequently encounter performance bottlenecks when dealing with complex queries, especially involving joins and pagination on tables containing hundreds of thousands of records. The scenario you described—pagination on 100k posts joined with 100k users, further complicated by inRandomOrder()—is a classic performance trap.

When Laravel’s Eloquent or the Query Builder struggles with these requests, it usually signals an inefficiency in the underlying SQL execution plan rather than a defect in the framework itself. Let's dive into why this happens and how we can architect solutions to achieve blazing-fast results.

The Root Cause: Why Pagination Slows Down

When you execute a query involving multiple tables (JOIN) and sorting on large result sets, the database engine must perform significant work before it can return the requested subset of data.

In your specific case:

  1. The Join: Joining posts and users immediately multiplies the number of rows that need to be processed.
  2. Filtering: Applying where('posts.status', '=', 1) filters this large set.
  3. Sorting (inRandomOrder()): This is the biggest performance killer here. When you ask for random ordering across a massive, joined dataset, the database cannot use simple index lookups efficiently if it has to calculate a full sort on potentially millions of intermediate rows before applying the LIMIT for pagination.

The bottleneck isn't necessarily Laravel; it’s how the underlying SQL query is structured and indexed.

Solution 1: The Foundation – Database Indexing

Before optimizing the Laravel code, we must optimize the database itself. Indexes are the single most critical factor in speeding up read operations on large tables.

For any table involved in joins or filtering, ensure you have appropriate indexes. Specifically, index the foreign keys used for joining and any columns used in WHERE clauses.

Example Indexing Strategy:

If you are working with migrations (which is best practice when using Laravel), ensure your indexes look something like this:

// In your migration file
Schema::table('posts', function (Blueprint $table) {
    $table->unsignedBigInteger('user_id')->index(); // Essential for the join
    $table->boolean('status');
});

Schema::table('users', function (Blueprint $table) {
    $table->boolean('status');
});

Proper indexing allows the database to jump directly to the relevant data instead of scanning every row, drastically reducing execution time. This principle is central to efficient data retrieval in any application, including those built with Laravel and following best practices outlined by resources like laravelcompany.com.

Solution 2: Optimizing the Query Structure (The Eloquent Approach)

While indexing handles the heavy lifting, we can refine how Eloquent constructs the query to be as lean as possible.

Instead of performing complex joins directly in a raw query when relationships exist, leverage Eloquent's relationship loading capabilities. If you are pulling data from a model, let Eloquent handle the join logic internally, which often results in cleaner and more optimized SQL generation.

Refactoring Example:

If your models have proper relationships set up, fetching related data through those methods is generally safer than manually writing complex join statements:

// Assuming Post model has a 'user' relationship
$posts = Post::where('status', 1)
    ->with('user') // Eager load the user relation
    ->orderBy(DB::raw('inRandomOrder()'))
    ->paginate(10);

If you are still using raw joins, ensure you are only selecting the necessary columns, avoiding the performance hit of retrieving unnecessary data:

$posts = Post::join('users', 'posts.user_id', 'users.id')
    ->where('posts.status', 1)
    ->where('users.status', 1)
    // Only select columns you actually need, not '*'
    ->select('posts.*', 'users.name') 
    ->orderBy(DB::raw('inRandomOrder()'))
    ->paginate(10);

Conclusion: Performance Through Planning

Slow performance on large datasets is rarely solved by a single trick; it requires a holistic approach centered on database efficiency. For pagination involving joins and random sorting on 100k+ records, the key takeaways are: Index everything you filter or join, use Eloquent relationships where possible, and be explicit about what data you select. By focusing on these principles, you ensure that your Laravel application leverages the power of the database optimally, resulting in significantly faster response times for your users.