Voltar ao Blog

Truques de Performance Eloquent: Fazendo Laravel Voar com Milhares de Registros

2024-02-05 * 9 min de leitura min Web Development
Quando você começa com Laravel, Eloquent parece mágica. Algumas linhas de código, e você pode consultar seu banco de dados com uma sintaxe bonita e expressiva. Mas conforme seu projeto cresce e você começa a lidar com milhares (ou até milhões) de linhas, essa mágica pode de repente se transformar em consultas lentas, picos de memória e páginas travando. A boa notícia? O ORM Eloquent do Laravel pode escalar muito bem - se você souber alguns truques de performance. Neste artigo, vou compartilhar as técnicas que ajudaram minha equipe e eu a otimizar aplicações em larga escala sem desistir da elegância do Eloquent.

1. Use select() para Recuperar Apenas o que Você Precisa

Por padrão, Eloquent recupera todas as colunas (SELECT *) do seu banco de dados. Para tabelas pequenas, isso está bem. Mas com tabelas grandes e esquemas amplos, isso desperdiça memória e desacelera consultas.
// ❌ Evitar: puxa todas as colunas
$users = User::all();

// ✅ Melhor: busca apenas os campos necessários
$users = User::select('id', 'name', 'email')->get();
👉 Esta pequena mudança pode economizar megabytes de memória em consultas grandes.

2. Processe Consultas Grandes em Lotes

Quando você precisa processar milhares de registros, não os carregue todos de uma vez. Use chunking para processá-los em lotes.
User::chunk(500, function ($users) {
    foreach ($users as $user) {
        // processar usuário
    }
});
Em vez de puxar 100k linhas para a memória, isso pega 500 por vez. Laravel cuida da paginação nos bastidores. Se a ordem importa (ex: para processamento consistente em lotes), não esqueça de ordenar por id.

3. Use lazy() ou cursor() para Streaming

Se você está trabalhando com conjuntos de dados realmente enormes, chunking pode ainda consumir muita memória. Nesse caso, use cursores:
foreach (User::cursor() as $user) {
    // Processar registros um por um
}
Isso evita carregar grandes lotes na memória, fazendo streaming das linhas diretamente do banco de dados. É mais lento por linha mas extremamente eficiente em memória.

4. Carregue Relacionamentos Antecipadamente (mas Não Sobrecarregue)

O problema de consulta N+1 é um dos maiores assassinos de performance em apps Laravel.
// ❌ Ruim: executa uma nova consulta para cada post
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->user->name;
}

// ✅ Bom: carrega usuários antecipadamente
$posts = Post::with('user')->get();
Isso reduz dezenas de consultas para apenas duas. ⚠️ Mas não carregue tudo antecipadamente. Carregue apenas os relacionamentos que você realmente precisa, senão você sobrecarregará a memória com dados não utilizados.

5. Indexe Suas Colunas do Banco de Dados

Às vezes, o gargalo não é Eloquent - é o próprio banco de dados. Adicionar índices apropriados pode cortar tempos de consulta de segundos para milissegundos.
Schema::table('users', function (Blueprint $table) {
    $table->index('email');
    $table->index(['status', 'created_at']);
});
👉 Sempre indexe colunas que são frequentemente usadas em cláusulas WHERE, ORDER BY, ou JOIN.

6. Pagine em Vez de Buscar Tudo

Para listas e dashboards, nunca carregue milhares de registros de uma vez. Em vez disso, pagine-os.
$users = User::paginate(50);
Isso te dá links de paginação limpos e mantém seu uso de memória minúsculo. Se você precisa de scroll infinito, simplePaginate() é ainda mais rápido.

7. Use update() e delete() em Massa

Se você precisa atualizar ou deletar muitos registros, não os percorra em loop. Faça em uma consulta.
// ❌ Ruim: atualiza linha por linha
foreach ($users as $user) {
    $user->update(['active' => false]);
}

// ✅ Bom: atualização em massa única
User::where('active', true)->update(['active' => false]);
Isso evita milhares de consultas e faz sua app voar.

8. Cache Quando Fizer Sentido

Se você está executando repetidamente a mesma consulta pesada, considere cachear os resultados.
$users = Cache::remember('active_users', 600, function () {
    return User::where('active', true)->get();
});
Laravel torna isso fácil, e cache pode transformar uma consulta DB lenta em uma busca de memória rápida.

9. Perfilando Consultas

Não adivinhe onde estão os gargalos. Use DB::enableQueryLog() ou Laravel Telescope para perfilar consultas.
DB::enableQueryLog();
$users = User::where('status', 'active')->get();
dd(DB::getQueryLog());
Ou use toSql() para ver o que Eloquent gera:
dd(User::where('status', 'active')->toSql());

10. Quando Necessário... Desça para Consultas Brutas

Eloquent é elegante, mas às vezes você precisa de performance bruta. Laravel torna fácil voltar ao query builder ou até SQL bruto.
// Query builder
$users = DB::table('users')->where('status', 'active')->get();

// SQL bruto
$users = DB::select('SELECT id, name FROM users WHERE status = ?', ['active']);
Misturar abordagens não é falha - é engenharia pragmática.

Conclusion

O ORM Eloquent do Laravel não é apenas para projetos pequenos. Com os truques certos - de chunking e cursores, a eager loading e updates em massa - você pode lidar com milhões de linhas eficientemente sem desistir da legibilidade e manutenibilidade que torna Laravel especial. No final do dia, otimização de performance no Laravel não é sobre abandonar Eloquent. É sobre entender suas forças, evitar suas armadilhas, e saber quando misturar poder bruto. E sim, mesmo depois de todos esses anos, eu ainda amo programar com Laravel. Ele cresceu comigo dos meus primeiros projetos paralelos ao meu papel como CTO hoje. Assim como o próprio PHP, ele se recusa a morrer - e honestamente, estou bem com isso. 🚀