2016-03-04 11 views
6

Sto tentando di inserire (in un database mySQL) dati da un file CSV "grande" (3Mo/37000 righe/7 colonne) utilizzando gli strumenti dati doctrine.Symfony: dispositivo dati doctrine: come gestire un file csv di grandi dimensioni?

Il processo è molto lento e in questo momento non potevo riuscire (potrebbe essere che ho dovuto aspettare un po 'di più).

Suppongo che gli infissi di dati di dottrina non siano destinati a gestire una tale quantità di dati? Forse la soluzione dovrebbe essere quella di importare direttamente il mio csv nel database?

Qualche idea su come procedere?

Ecco il codice:

<?php 

namespace FBN\GuideBundle\DataFixtures\ORM; 

use Doctrine\Common\DataFixtures\AbstractFixture; 
use Doctrine\Common\DataFixtures\OrderedFixtureInterface; 
use Doctrine\Common\Persistence\ObjectManager; 
use FBN\GuideBundle\Entity\CoordinatesFRCity as CoordFRCity; 

class CoordinatesFRCity extends AbstractFixture implements OrderedFixtureInterface 
{ 
    public function load(ObjectManager $manager) 
    { 
     $csv = fopen(dirname(__FILE__).'/Resources/Coordinates/CoordinatesFRCity.csv', 'r'); 

     $i = 0; 

     while (!feof($csv)) { 
      $line = fgetcsv($csv); 

      $coordinatesfrcity[$i] = new CoordFRCity(); 
      $coordinatesfrcity[$i]->setAreaPre2016($line[0]); 
      $coordinatesfrcity[$i]->setAreaPost2016($line[1]); 
      $coordinatesfrcity[$i]->setDeptNum($line[2]); 
      $coordinatesfrcity[$i]->setDeptName($line[3]); 
      $coordinatesfrcity[$i]->setdistrict($line[4]); 
      $coordinatesfrcity[$i]->setpostCode($line[5]); 
      $coordinatesfrcity[$i]->setCity($line[6]); 

      $manager->persist($coordinatesfrcity[$i]); 

      $this->addReference('coordinatesfrcity-'.$i, $coordinatesfrcity[$i]); 


      $i = $i + 1; 
     } 

     fclose($csv); 

     $manager->flush(); 
    } 

    public function getOrder() 
    { 
     return 1; 
    } 
} 

risposta

6

due regole da seguire quando si crea grandi importazioni di batch come questo:

  • disattivare la registrazione SQL: ($manager->getConnection()->getConfiguration()->setSQLLogger(null);) per evitare la perdita enorme di memoria.

  • Lavare e cancellare frequentemente invece di una sola volta alla fine. Ti suggerisco di aggiungere if ($i % 25 == 0) { $manager->flush(); $manager->clear() } all'interno del tuo loop, per svuotare ogni 25 INSERT.

EDIT: Un'ultima cosa che ho dimenticato: non mantenere le entità all'interno di variabili quando non hai bisogno di più di loro. Qui, nel tuo ciclo, hai solo bisogno dell'entità corrente che viene elaborata, quindi non memorizzare l'entità precedente in una matrice $coordinatesfrcity. Questo potrebbe portare a un eccesso di memoria se continui a farlo.

+0

Grazie. Avevo provato a lavare ogni inserto ma era una frequenza troppo importante, suppongo. Proverò la tua proposta. Scusa ma cos'è il log SQL? – Cruz

+0

Doctrine viene fornito con un sistema di registrazione che conserva le tracce delle query SQL eseguite. In questo contesto, non ne avrai bisogno e sarà un costo inutile della memoria. – Terenoth

+0

Ok capisco cos'è la registrazione SQL. E hai ragione, questo tavolo proviene da un altro pezzo di codice e non è necessario qui. – Cruz

0

V'è un grande esempio nei documenti: http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html

Utilizzare un modulo (x% a) espressione per implementare l'elaborazione batch, questo esempio inserirà 20 alla volta. Potresti essere in grado di ottimizzare questo a seconda del tuo server.

$batchSize = 20; 
for ($i = 1; $i <= 10000; ++$i) { 
    $user = new CmsUser; 
    $user->setStatus('user'); 
    $user->setUsername('user' . $i); 
    $user->setName('Mr.Smith-' . $i); 
    $em->persist($user); 
    if (($i % $batchSize) === 0) { 
     $em->flush(); 
     $em->clear(); // Detaches all objects from Doctrine! 
    } 
} 
$em->flush(); //Persist objects that did not make up an entire batch 
$em->clear(); 
+0

Grazie. Questa è la stessa risposta di Sogara. Ci proverò il prima possibile. – Cruz

+0

Ero sulla pagina dei documenti l'altro giorno con un problema simile, pensavo di darti una fonte come l'avevo a portata di mano. – Sarcoma

+0

Grazie per quello! – Cruz

0

Per apparecchi che necessitano di grandi quantità di memoria, ma non dipendono gli uni sugli altri, ho questo problema utilizzando il flag di accodamento per inserire un'entità (o più piccolo gruppo di entità) alla volta:

bin/console doctrine:fixtures:load --fixtures="memory_hungry_fixture.file" --append 

Quindi scrivo uno script Bash che esegue quel comando quante volte ho bisogno.

Nel tuo caso, si potrebbe estendere il comando Fixtures e una bandiera che fa lotti di soggetti - le prime 1000 righe, poi il 2 ° 1000, ecc

Problemi correlati