2010-01-19 13 views
16

Tengo problemas con la inserción de objetos en una base de datos por lotes utilizando Symfony 1.4 y doctrine 1.2.php/symfony/doctrine memory leak?

Mi modelo tiene un cierto tipo de objeto llamado "Sector", cada uno de los cuales tiene varios objetos de tipo "Cupo" (generalmente desde 50 hasta 200000). Estos objetos son bastante pequeños; solo una cadena identificativa corta y uno o dos enteros. Cuando el usuario crea un grupo de Sectores, necesito agregar automáticamente todas estas instancias de "Cupo" a la base de datos. En caso de que algo salga mal, estoy usando una transacción de doctrina para deshacer todo. El problema es que solo puedo crear unas 2000 instancias antes de que php se quede sin memoria. Actualmente tiene un límite de 128 MB, que debería ser más que suficiente para manejar objetos que usan menos de 100 bytes. Intenté aumentar el límite de memoria de hasta 512 MB, pero php aún falla y eso no resuelve el problema. ¿Estoy haciendo la inserción del lote correctamente o hay una mejor manera?

Aquí está el error:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 71 bytes) in /Users/yo/Sites/grifoo/lib/vendor/symfony/lib/log/sfVarLogger.class.php on line 170 

Y aquí está el código:

public function save($conn=null){ 

    $conn=$conn?$conn:Doctrine_Manager::connection(); 

    $conn->beginTransaction(); 


    try { 
     $evento=$this->object; 


     foreach($evento->getSectores() as $s){ 

      for($j=0;$j<$s->getCapacity();$j++){ 

       $cupo=new Cupo(); 
       $cupo->setActivo($s->getActivo()); 
       $cupo->setEventoId($s->getEventoId()); 
       $cupo->setNombre($j); 
       $cupo->setSector($s); 

       $cupo->save(); 

      } 
     } 

     $conn->commit(); 
     return; 
    } 
    catch (Exception $e) { 
     $conn->rollback(); 
     throw $e; 
    } 

Una vez más, este código funciona bien por menos de 1.000 objetos, pero nada más grande que 1.500 falla. Gracias por la ayuda.

+1

marque la respuesta de alguien como correcta, ¿eh? – develop7

+0

Para inserciones en bloque, también puede considerar realizar una inserción de SQL sin procesar a través de PDO, sin duda será más rápido y no perderá memoria. –

Respuesta

1

Intente unset($cupo); después de cada ahorro. Esto debería ser de ayuda. Otra cosa es dividir el script y hacer un procesamiento por lotes.

1

tratar de romper referencia circular que suele causar pérdidas de memoria con

$cupo->save(); 

$cupo->free(); //this call 

como described en el manual Doctrina.

0

Cierre y vuelva a abrir periódicamente la conexión; no estoy seguro por qué pero parece que PDO retiene las referencias.

3

He acaba de hacer "endemoniada" guión con Symfony 1.4 y el establecimiento de la siguiente detenido la memoria acaparando:

sfConfig::set('sf_debug', false); 
+0

Esto funcionó para mí si lo hace antes de crear el administrador de la base de datos. Si estás en una tarea, creo que esto es seguro. –

33

intentado hacer

$cupo->save(); 
$cupo->free(); 
$cupo = null; 

(pero sustituyendo el código) Y estoy sigue recibiendo desbordamientos de memoria. Alguna otra idea, ¿ASÍ?

Actualización:

he creado un nuevo entorno en mi databases.yml, que se parece a:

all: 
    doctrine: 
    class: sfDoctrineDatabase 
    param: 
     dsn: 'mysql:host=localhost;dbname=.......' 
     username: ..... 
     password: ..... 
     profiler: false 

El perfilador: falsa entrada deshabilita el registro de consultas de la doctrina, que normalmente mantiene una copia de cada consulta que realice. No detuvo la fuga de memoria, pero pude obtener el doble de lo que importaba importar mis datos sin él.

Actualización 2

que añade

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

antes de ejecutar mis preguntas, y le cambió

$cupo = null; 

a

unset($cupo); 

Y ahora mi guión ha estado revoloteando felizmente. Estoy bastante seguro de que terminará sin quedarse sin RAM esta vez.

Actualización 3

Sip. Esa es la combinación ganadora.

+1

Tengo un problema similar. Intenté estas cosas y mi rendimiento mejoró significativamente. –

+0

Si es posible, actualice a php5.3 y use -> free(). Las pérdidas de memoria se deben a que el recolector de basura de PHP5.2 no puede eliminar las referencias circulares a las que ya no hace referencia desde fuera de su ciclo. PHP5.3 tiene un mejor recopilador, y free() intenta deshacer esas referencias antes de que la variable abandone el alcance. –

+1

@Jordan - ¡GUAU! Estaba teniendo un problema similar, pero en un perfil selecto: falso tuvo un GRAN impacto ... gracias – ManseUK

2

Doctrine gotea y no hay mucho que pueda hacer al respecto. Asegúrese de usar $ q-> free() siempre que sea aplicable para minimizar el efecto. Doctrine no está destinado a los scripts de mantenimiento. La única forma de evitar este problema es dividir el script en partes que realizarán parte de la tarea. Una forma de hacerlo es agregar un parámetro de inicio a su secuencia de comandos y después de que se haya procesado una cierta cantidad de objetos, la secuencia de comandos se redirige a sí misma con un valor de inicio más alto. Esto funciona bien para mí, aunque hace que escribir guiones de mantenimiento sea más engorroso.

+0

He hecho la misma experiencia que todos esos pequeños trucos realmente no ayudan. Esbocé un enfoque similar al tuyo: http://stackoverflow.com/a/11474869/620410 – Tapper

0

¿Qué está funcionando para mí está llamando el método free así:

$cupo->save(); 
$cupo->free(true); // free also the related components 
unset($cupo); 
3

Para las tareas de Symfony, también me enfrenté a este problema y he hecho las cosas siguientes. Funcionó para mí

  • Deshabilitar el modo de depuración. Añadir siguiente antes de la conexión db inicializar atributos libre

    sfConfig::set('sf_debug', false); 
    
  • Conjunto de auto consulta de objeto para la conexión db

    $connection->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true); 
    
  • libre de todos los objetos después de su uso

    $object_name->free() 
    
  • Destruye todas las matrices después de su uso unset($array_name)

  • Che ck todas las consultas de doctrina utilizadas en la tarea. Gratis todas las consultas después de su uso. $q->free() (Esta es una buena práctica para cualquier momento de la consulta utilizando.)

eso es todo. Espero que pueda ayudar a alguien.

1

Para mí, acabo de inicializar la tarea de esa manera:

// initialize the database connection 
$databaseManager = new sfDatabaseManager($this->configuration); 
$connection = $databaseManager->getDatabase($options['connection'])->getConnection(); 
$config = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', true); 
sfContext::createInstance($config); 

(CON PROD CONFIG)
y el uso libre() después de la parada() en el objeto de la doctrina

la memoria es estable a 25Mo

memory_get_usage=26.884071350098Mo 

con php 5.3 en squeeze debian