2010-03-17 8 views
17

Ich benutze Symfony 1.4 und Doctrine.Wie verwende ich weniger Speicher beim Ausführen einer Aufgabe in Symfony 1.4?

Bisher hatte ich keine Probleme mit Symfony. Aber jetzt, wo ich eine ziemlich große Menge an Daten importieren und sie in der Datenbank speichern, erhalte ich die berüchtigten

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

Während dieser Import ich neue Objekte nur bin zu schaffen, ein paar Felder setzen und sie zu speichern .

Ich bin ziemlich sicher, es hat etwas mit der Anzahl der Objekte zu tun, die ich beim Speichern von Daten erstellen. Das Deaktivieren dieser Objekte bewirkt jedoch nichts.

Gibt es Best Practices zur Begrenzung der Speichernutzung in Symfony?

+1

+1 gute Frage; Es ist etwas, was in den Symfony-Dokumenten nicht wirklich behandelt wird! – richsage

+0

Eine andere Antwort, um Speicherverlust zu vermeiden http://stackoverflow.com/a/4066680/569101 – j0k

Antwort

11

Ich bin darauf gestoßen, und es gibt ein paar Techniken, die mir bei Doctrines umfangreicher Speichernutzung sehr geholfen haben.

1: Wenn möglich, hydratieren Sie Ihre Doctrine-Abfrageergebnisse in ein Array. Sie können dies wie folgt zB:

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

Diese Kräfte Lehre NICHT große Objekte zu erstellen, sondern reduziert sie auf ein Array. Beachten Sie, dass Sie dadurch die Möglichkeit verlieren, Methoden usw. aufzurufen. Dies ist nur sinnvoll, wenn Sie diese zum Lesen von Feldwerten usw. verwenden.

2: Geben Sie Ihre Ergebnisse nach der Ausführung frei. Dies wird in einem kleinen Bereich der Lehre docs dokumentiert, aber es ist wirklich die Importaufgabe hilft ich wurde mit:

$query->free(); 

Das ist es. Sie können dies auch für Objekte tun, die Sie erstellt haben, z. B. $myObj->free();, und dies zwingt Doctrine, alle von ihm erstellten Zirkelverweise zu entfernen. Beachten Sie, dass zirkuläre Referenzen automatisch von PHP 5.3 beim Löschen eines Objekts über den PHP-Bereich oder unset() befreit werden, aber vorher müssen Sie es selbst tun.

Das Entfernen von Variablen, nachdem Sie sie verwendet haben, hilft auch, obwohl dies in Verbindung mit der free() Methode oben erwähnt, wie unset() wird nicht die kreisförmigen refs sonst löschen.

+0

free() hilft viel, danke! –

0

Auch lohnt ein Blick in:

gc_collect_cycles - Forces Sammlung aller vorhandenen Müllzyklen

+0

vorher sollten Sie gc => gc_enable() aktivieren – kirugan

1

ich das gleiche Problem mit PHP Batch-Jobs für symfony gehabt haben - wenn sie laufen für eine lange Zeit und Einsatz eine Menge Daten neigen dazu, sich zu ballonieren, und selbst wenn ich einen Wrapper erstellt habe, der viele separate PHP-Prozesse aufgerufen hat, hat es nicht geholfen.

Aus diesem Grund habe ich meine größeren Batch-Jobs mit Perls DBI umgeschrieben und sie sind zuverlässig und überschaubar.

Ich schlage nicht vor, das ist die beste Antwort, nur sympathisieren und meine Erfahrung anbieten. Es gibt wahrscheinlich einen Weg, um PHP besser zu machen.

2

Sorry ich weiß, das ist eine späte Antwort, aber könnte jemandem helfen.

Ein weiterer großer Speichersparmodus ist, um sicherzustellen, dass Symfonys Debug-Modus für diese Aufgabe nicht aktiviert ist.In ein paar langwierigen Aufgaben habe ich diese Zeile hinzugefügt und die RAM-Auslastung um etwa 40% reduziert, auch nachdem ich Dinge wie den Hydrationsmodus optimiert hatte.

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

Try this:

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

wie erwähnt auf

php/symfony/doctrine memory leak?

Antwort von Jordan Feldstein nicht meine.

4

Ein weiterer Hinweis zur Reduzierung der Speicherkapazität in einer Task ist die Deaktivierung des Abfrageprofilers. Eine große Anzahl von Abfragen führt dazu, dass die Task mehr und mehr Speicher verwendet.

Um erzeuge so eine neue Task-Umgebung in Ihrer database.yml config-Datei, indem Sie die folgenden Zeilen hinzu:

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

Dann Setup Ihre Aufgabe in "Aufgabe" Umgebung ausgeführt werden. Es sollte helfen, die Speichernutzung stabil zu halten, wenn sich Ihre Abfragen in einer Schleife befinden.

0

Versuchen Sie auch, die (Auswahl-) Felder in Ihrer Abfrage auf diejenigen zu beschränken, die Sie wirklich benötigen.

zum Beispiel der Verwendung so etwas wie:

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

statt:

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

Vorsicht mit fetchOne() auf Lehre Abfrage. Dieser Funktionsaufruf wird nicht anhängen „Limit 1“ auf SQL

Wenn Sie nur sicher brauchen, um eine Datensätze aus DB zu erhalten, stellen:

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

Die Speichernutzung ist enorm auf großen Tisch fallen gelassen.

Sie konnten sehen, dass fetchOne() zuerst von der DB als Sammlung abrufen und dann das erste Element zurückgeben wird.

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; 
}