2010-03-17 9 views
17

Używam Symfony 1.4 i Doctrine.Jak używać mniej pamięci podczas uruchamiania zadania w Symfony 1.4?

Do tej pory nie miałem problemu z uruchamianiem zadań w Symfony. Ale teraz, że muszę zaimportować dość duże ilości danych i zapisanie ich w bazie danych, pojawia się niesławny

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

Podczas importu tworzę tylko nowe obiekty, wyznaczając kilka pól i zapisaniu .

Jestem prawie pewien, że ma to coś wspólnego z liczbą obiektów, które tworzę podczas zapisywania danych. Odzbijanie tych obiektów nie robi jednak nic.

Czy są jakieś sprawdzone metody ograniczania zużycia pamięci w Symfony?

+1

+1 dobre pytanie; to jest coś, czego naprawdę nie ma w dokumentach Symfony! – richsage

+0

Inna odpowiedź, aby uniknąć wycieku pamięci http://stackoverflow.com/a/4066680/569101 – j0k

Odpowiedz

11

Natknąłem się na to i istnieje kilka technik, które naprawdę pomogły mi w wykorzystaniu rozległej pamięci Doctrine.

1: Tam, gdzie to możliwe, nawadnij wyniki zapytania Doctrine do tablicy. Można to zrobić w następujący sposób np

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

Zmusza Doctrine nie tworzenie dużych obiektów, lecz ogranicza je do tablicy. Oczywiście pamiętaj, że jeśli to zrobisz, stracisz możliwość wywoływania metod itd., Więc jest to dobre tylko wtedy, gdy używasz go do odczytywania wartości pól itp.

2: Uwolnij wyniki po wykonaniu. Jest to udokumentowane w maleńkim obszarze docs doktrynę, ale to naprawdę pomógł zadanie import używałem:

$query->free(); 

to wszystko. Możesz to również zrobić na stworzonych obiektach, np. $myObj->free();, co zmusza Doctrine do usunięcia wszystkich utworzonych przez nią odwołań kołowych. Zwróć uwagę, że odwołania kołowe są automatycznie zwalniane z PHP 5.3 i później w przypadku usuwania obiektu przez zakres PHP lub unset(), ale wcześniej musisz to zrobić samodzielnie.

Wyłączenie zmiennych po ich użyciu również pomaga, chociaż należy to zrobić w połączeniu z powyższą metodą free(), ponieważ w przeciwnym razie unset() nie usunie znaków okrągłych.

+0

free() bardzo pomaga, dzięki! –

0

Warto również rozważają:

gc_collect_cycles - siły zbiórki wszelkich istniejących cykli śmieci

+0

wcześniej należy włączyć gc => gc_enable() – kirugan

1

miałem ten sam problem z zadań wsadowych PHP Symfony - jeśli działają one przez długi czas i użytkowania wiele danych ma tendencję do balansowania, a nawet jeśli zrobiłem jedno opakowanie, które wywołało wiele oddzielnych procesów PHP, to nie pomogło.

Z tego powodu przepisałem moje większe zadania wsadowe za pomocą interfejsu DBI firmy Perl i są one niezawodne i łatwe w zarządzaniu.

Nie sugeruję, że jest to najlepsza odpowiedź, tylko sympatyzuję i oferuję swoje doświadczenie. Prawdopodobnie istnieje sposób, aby PHP działało lepiej.

2

Przepraszam, wiem, że to spóźniona odpowiedź, ale ktoś może pomóc.

Kolejnym potencjalnie dużym oszczędzaniem pamięci jest upewnienie się, że tryb debugowania Symfony nie jest włączony dla tego zadania.W ciągu kilku długotrwałych zadań dodałem tę linię i zmniejszyłem zużycie pamięci RAM o około 40%, nawet po zoptymalizowaniu trybu nawodnienia.

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

Spróbuj tego:

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

jak wspomniano na

php/symfony/doctrine memory leak?

Odpowiedź od Jordan Feldstein nie moja.

4

Inną wskazówką dotyczącą zmniejszenia ilości pamięci używanej w zadaniu jest wyłączenie profilera zapytań. Duża liczba zapytań sprawia, że ​​zadanie wykorzystuje coraz więcej pamięci.

zrobić więc stworzyć nowe środowisko zadanie w pliku konfiguracyjnym database.yml dodając następujące linie:

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

następnie skonfigurować swoje zadanie do uruchomienia w środowisku „zadania”. Powinno to pomóc w utrzymaniu stabilności użycia pamięci, jeśli zapytania są w pętli.

0

Spróbuj również ograniczyć (wybierz) pola w zapytaniu tylko do tych, których naprawdę potrzebujesz.

na przykład użycie coś jak:

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

zamiast:

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

ostrożność fetchOne() na Doctrine Query. To wywołanie funkcji nie doda „Granica 1” na SQL

Jeśli wystarczy, aby jeden rekordy od DB, upewnij się, że:

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

Pamięć użycie jest ogromny spadł na dużym stole.

Możesz zobaczyć, że fetchOne() pobierze z DB jako kolekcję, a następnie zwróci pierwszy element.

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; 
} 
Powiązane problemy