NOTAS WEB
Degradación de Rendimiento en Doctrine

Evitando la Degradación de Rendimiento en Procesos Masivos con Doctrine y Symfony
Procesar grandes volúmenes de datos en Symfony puede parecer sencillo… hasta que notás que tu script, que al principio volaba, ahora tarda una eternidad y consume RAM como si no hubiera un mañana 🐘.
En esta entrada te explico por qué ocurre este problema, y cómo resolverlo aplicando una estrategia de procesamiento en lotes (batch processing) con Doctrine.
El Problema
Doctrine ORM es poderoso, pero tiene un comportamiento que si no entendés, puede matar tu rendimiento en tareas pesadas.
Cuando hacés:
persist()
para agregar entidades, ofind()
para consultar entidades,
Doctrine los guarda internamente en el EntityManager, dentro de una estructura llamada Unit of Work. Esto le permite hacer seguimiento de los cambios y sincronizarlos al ejecutar flush()
.
Pero hay un gran pero…
A medida que procesás más y más entidades:
flush()
se vuelve más lento: compara cada entidad conocida para ver qué cambió.- Aumenta el uso de memoria: Doctrine no libera nada automáticamente.
- Las consultas nuevas se mezclan con el estado del Unit of Work, complicando todo
Ejemplo clásico del problema
Supongamos que estás procesando 1000 líneas de un fichero, y por cada línea hacés lo siguiente:
$line = $repository->find($id);
$line->setStatus('processed');
$entityManager->flush();
Después de 1000 iteraciones, tu EntityManager tiene 1000 objetos en memoria. El rendimiento de flush()
va bajando… y el uso de RAM subiendo 🚀
La Solución: Batch Processing
La estrategia ganadora es:
Guardar y limpiar en lotes pequeños, usando
flush()
yclear()
cada N elementos.
Esto limita el tamaño del Unit of Work, reduciendo el trabajo de Doctrine y liberando memoria progresivamente.
Ejemplo de implementación
Procesar entidades en lotes de 50
$batchSize = 50;
$i = 0;
foreach ($lineIds as $lineId) {
$line = $this->processLineRepository->findByProcessLineId($lineId);
if (!$line || !in_array($line->getStatus(), ['pending', 'scheduled'])) {
continue;
}
// Procesamiento
$line->setStatus('processed');
$this->entityManager->persist($line);
// Cada 50 líneas, hacemos flush y clear
if (++$i % $batchSize === 0) {
$this->flushAndClear($i);
}
}
// Final flush para el remanente
$this->flushAndClear($i);
Función flushAndClear()
con monitoreo de memoria
private int $memStart;
public function __construct(EntityManagerInterface $em, LoggerInterface $logger)
{
$this->entityManager = $em;
$this->logger = $logger;
$this->memStart = memory_get_usage();
}
private function flushAndClear(int $i): void
{
$used = memory_get_usage() - $this->memStart;
$avg = $used / $i;
$this->logger->info(sprintf(
'>> Memoria usada: %s | Promedio por iteración: %s',
round($used / 1024 / 1024, 2).' MB',
round($avg / 1024, 2).' KB'
));
$this->entityManager->flush();
$this->entityManager->clear();
gc_collect_cycles(); // Forzamos limpieza de ciclos
}
Beneficios reales
flush()
es mucho más rápido (procesa solo 50 entidades en lugar de 1000).- Liberás memoria constantemente.
- Evitás que Doctrine realice trabajo innecesario.
- Tu aplicación puede procesar millones de registros sin degradación.
Bonus: ¿Qué pasa si necesitás una entidad después del clear()
?
Recuerda que clear()
borra todas las entidades del EntityManager. Si necesitás seguir trabajando con alguna después de hacer clear()
, puedes:
$lineId = $line->getId(); // antes del clear
$this->entityManager->clear();
$line = $this->processLineRepository->findByProcessLineId($lineId); // recargar
Recursos recomendados
- Symfony Docs: Doctrine Best Practices
- Doctrine Docs: Batch Processing
- Libro: Clean Code (Robert C. Martin) — Capítulo de optimización y ciclos de vida de objetos.
Si estás usando Symfony y Doctrine para procesar grandes volúmenes de datos, flush()
y clear()
por lotes es obligatorio. No solo mejora el rendimiento, sino que evita problemas de memoria y cuelgues silenciosos.

Desarrollador de software con más de 7 años de experiencia, especializado en desarrollo web y backend. Con habilidades demostradas en PHP, Laravel, Symfony, y una amplia gama de tecnologías modernas. Apasionado por el diseño y desarrollo de software.