2010-03-17 18 views
17

Estoy usando Symfony 1.4 y Doctrine.¿Cómo se usa menos memoria mientras se ejecuta una tarea en Symfony 1.4?

Hasta ahora no tuve problemas para ejecutar tareas con Symfony. Pero ahora que tengo para importar una muy grande cantidad de datos y guardarlos en la base de datos, me sale el famoso

"Fatal Error: Allowed memory size of XXXX bytes exhausted"

Durante esta importación sólo estoy creando nuevos objetos, el establecimiento de unos campos y guardarlos .

Estoy bastante seguro de que tiene algo que ver con la cantidad de objetos que estoy creando al guardar datos. Deshacer esos objetos no hace nada.

¿Existen algunas prácticas recomendadas para limitar el uso de la memoria en Symfony?

+1

+1 buena pregunta; ¡es algo que realmente no está cubierto en los documentos de Symfony! – richsage

+0

Otra respuesta para evitar la pérdida de memoria http://stackoverflow.com/a/4066680/569101 – j0k

Respuesta

11

Me he encontrado con esto, y hay un par de técnicas que encontré realmente ayudaron con el uso extensivo de memoria de Doctrine.

1: Donde sea posible, hidrate los resultados de su consulta de Doctrine en una matriz. Puede hacerlo de la siguiente ejemplo:

$query = self::createQuery("q")-> 
    ... 
    ->setHydrationMode(Doctrine::HYDRATE_ARRAY) 
    ->execute(); 

Esta Doctrina fuerzas para no crear objetos de gran tamaño, pero en cambio se reduce a una matriz. Obviamente, tenga en cuenta que si hace esto, perderá la capacidad de llamar a métodos, etc., así que esto solo es bueno si lo está usando para leer valores de campo, etc.

2: Libere los resultados después de la ejecución. Esto está documentado en una pequeña área de la documentación de la doctrina, pero en realidad ayudó a cabo la tarea de importación que estaba usando:

$query->free(); 

Eso es todo. También puede hacer esto en los objetos que ha creado, por ejemplo, $myObj->free(); y esto fuerza a Doctrine a eliminar todas las referencias circulares que crea. Tenga en cuenta que las referencias circulares se liberan automáticamente desde PHP 5.3 en adelante al eliminar un objeto mediante el alcance de PHP o unset(), pero antes deberá hacerlo usted mismo.

Desarmar variables después de que las haya usado también ayuda, aunque haga esto en conjunto con el método anterior free() como se mencionó, como unset() no borrará las referencias circulares de lo contrario.

+0

gratis() ayuda mucho, ¡gracias! –

0

También vale la pena analizar:

gc_collect_cycles - Fuerzas de recolección de cualquier ciclo de basura existentes

+0

antes de eso, debería habilitar gc => gc_enable() – kirugan

1

que he tenido el mismo problema con PHP trabajos de lote para Symfony - si se quedan durante mucho tiempo y el uso una gran cantidad de datos que tienden a inflar, e incluso si hice un contenedor que invoca muchos procesos PHP separados, no ayudó.

Debido a esto, he reescrito mis trabajos por lotes más grandes con DBI de Perl y son confiables y manejables.

No estoy sugiriendo que esta sea la mejor respuesta, simpatizando y ofreciendo mi experiencia. Probablemente haya una manera de hacer que PHP se comporte mejor.

2

Lo siento, sé que esta es una respuesta tardía, pero podría ayudar a alguien.

Otro protector de memoria potencialmente enorme es asegurarse de que el modo de depuración de Symfony no esté habilitado para esa tarea.En un par de tareas de larga duración agregué esta línea y redujo el uso de la RAM en un 40% incluso después de haber optimizado cosas como el modo de hidratación.

sfConfig::set('sf_debug', false); 
4

Prueba esto:

Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true); 

como se ha mencionado en

php/symfony/doctrine memory leak?

respuesta de Jordan Feldstein no la mía.

4

Otra sugerencia para reducir la cantidad de memoria utilizada dentro de una tarea es deshabilitar el generador de perfiles de consulta. Una gran cantidad de consultas tiende a hacer que la tarea use más y más memoria.

para hacerlo crear un nuevo entorno de trabajo en el archivo de configuración database.yml añadiendo las siguientes líneas:

task: 
    doctrine: 
    class: sfDoctrineDatabase 
    param: 
     profiler: false 

A continuación, la configuración de la tarea se ejecute en el entorno de "tareas". Debería ayudar a mantener el uso de memoria estable si sus consultas están en un bucle.

0

También intente limitar los campos (de selección) en su consulta solo a aquellos que realmente necesita.

por ejemplo, usar algo como:

$query = self::createQuery("q")-> 
    ->select('id','title','price') 
    ... 

en lugar de:

$query = self::createQuery("q")-> 
    ->select('*') 
    ... 
1

Precaución con fetchOne() en la Doctrina de consulta. Esta llamada de función no añadir "Límite 1" en SQL

Si sólo tiene que conseguir uno de los registros de base de datos, asegúrese de que:

$q->limit(1)->fetchOne() 

El uso de memoria es tremendo caer sobre una mesa grande.

Puede ver que fetchOne() buscará primero DB como una colección y luego devolverá el primer elemento.

public function fetchOne($params = array(), $hydrationMode = null) 
{ 
    $collection = $this->execute($params, $hydrationMode); 

    if (is_scalar($collection)) { 
     return $collection; 
    } 

    if (count($collection) === 0) { 
     return false; 
    } 

    if ($collection instanceof Doctrine_Collection) { 
     return $collection->getFirst(); 
    } else if (is_array($collection)) { 
     return array_shift($collection); 
    } 

    return false; 
} 
Cuestiones relacionadas