Laravel chunk() vs cursor() vs lazy() — Handle Large Data Without Crashing Your Server

Published: (May 28, 2026 at 09:55 PM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Laravel large data handling

Introduction

If you’ve ever tried to process thousands of rows in Laravel and got a memory error or server timeout — this article is for you. I learned this the hard way when a large CSV export caused Apache timeout errors in production. Here’s what I found out.

The Problem

// ❌ This will crash on large tables
$users = User::all();
foreach ($users as $user) {
    // process...
}

all() loads every row into memory at once. On 100,000+ rows, your server will run out of memory.

chunk() — Process in Batches

User::chunk(500, function ($users) {
    foreach ($users as $user) {
        // processes 500 rows at a time
    }
});
  • ✅ Memory stays low
  • ✅ Good for background jobs
  • ⚠️ Don’t modify/delete rows inside chunk() — it can skip records
  • ⚠️ Runs multiple SQL queries

Use when: sending emails in batches, background processing.

chunkById() — Safer Version of chunk()

User::chunkById(500, function ($users) {
    foreach ($users as $user) {
        // safe even when updating rows
    }
});
  • ✅ Safe to update/delete rows inside
  • ✅ More reliable than chunk()

Use when: updating or deleting large amounts of records.

cursor() — One Row at a Time

foreach (User::cursor() as $user) {
    // processes one row at a time
}
  • ✅ Most memory efficient
  • ✅ Only one SQL query
  • ❌ Can’t use eager loading (with())
  • ❌ Keeps DB connection open the whole time

Use when: read‑only processing, CSV exports.

lazy() — Best of Both Worlds

foreach (User::lazy() as $user) {
    // chunks behind the scenes, feels like cursor()
}
  • ✅ Memory efficient
  • ✅ Supports eager loading with with()
  • ✅ Cleaner syntax
User::with('orders')->lazy()->each(function ($user) {
    // process
});

Use when: you need cursor()‑style iteration and need relationships loaded.

Quick Comparison Table

MethodMemorySQL QueriesSupports with()Safe to modify?
all()❌ High1
chunk()✅ LowMultiple⚠️ No
chunkById()✅ LowMultiple✅ Yes
cursor()✅ Lowest1
lazy()✅ LowMultiple

My Real‑World Example

This fixed my Apache timeout issue on CSV export:

// ❌ Before — caused timeout
$users = User::all();

// ✅ After — works perfectly
foreach (User::cursor() as $user) {
    fputcsv($handle, [
        $user->name,
        $user->email,
        $user->created_at
    ]);
}

Summary

  • chunk() — batch jobs, email sending
  • chunkById() — when modifying data in batches
  • cursor() — read‑only, CSV exports, most memory efficient
  • lazy() — when you need cursor() + relationships

Want to dive deeper? Check out the full lesson on my free Laravel tutorial site:

https://php-laravel-tutorials.netlify.app/lesson-laravel-large-data

0 views
Back to Blog

Related posts

Read more »