Как использовать меньше памяти при выполнении задачи в Symfony 1.4? - PullRequest
17 голосов
/ 17 марта 2010

Я использую Symfony 1.4 и Doctrine.

До сих пор у меня не было проблем с запуском задач с Symfony. Но теперь, когда мне нужно импортировать довольно большой объем данных и сохранить их в базе данных, я получаю печально известную

"Неустранимая ошибка: допустимый объем памяти XXXX байт исчерпано "

Во время этого импорта я только создаю новые объекты, настраиваю несколько полей и сохраняю их.

Я почти уверен, что это как-то связано с количеством объектов, которые я создаю при сохранении данных. Отключение этих объектов ничего не делает.

Есть ли рекомендации по ограничению использования памяти в Symfony?

Ответы [ 8 ]

11 голосов
/ 17 марта 2010

Я сталкивался с этим, и я нашел несколько техник, которые действительно помогли с большим использованием памяти в Doctrine.

1: где возможно, упорядочьте результаты запроса Doctrine до массива. Вы можете сделать это следующим образом, например:

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

Это заставляет Doctrine НЕ создавать большие объекты, а вместо этого уменьшает его до массива. Очевидно, имейте в виду, что если вы сделаете это, вы потеряете способность вызывать методы и т. Д., Так что это хорошо, только если вы используете это для чтения значений полей и т. Д.

2: освободить результаты после выполнения. Это задокументировано в крошечной области документации по Doctrine, но это действительно помогло выполнить задачу импорта, которую я использовал:

$query->free();

Вот и все. Вы также можете сделать это на объектах, которые вы создали, например $myObj->free();, и это заставит Doctrine удалить все циклические ссылки, которые он создает. Обратите внимание, что циклические ссылки автоматически удаляются из PHP 5.3 и далее при удалении объекта через область видимости PHP или unset(), но перед этим вам нужно будет сделать это самостоятельно.

Сброс переменных после того, как вы их использовали, также помогает, хотя делайте это в сочетании с вышеописанным методом free(), так как unset() не очистит круговые ссылки в противном случае.

4 голосов
/ 27 апреля 2012

Еще одна подсказка для уменьшения объема памяти, используемой внутри задачи, - отключить профилировщик запросов. Большое количество запросов приводит к тому, что задача использует все больше и больше памяти.

Для этого создайте новую среду задач в файле конфигурации database.yml, добавив следующие строки:

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

Затем настройте задачу для запуска в среде «задачи». Это должно помочь сохранить стабильное использование памяти, если ваши запросы находятся в цикле.

4 голосов
/ 11 июля 2011

Попробуйте это:

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

как указано на

утечка памяти в php / symfony / доктрине?

Ответ Джордана Фельдштейна не мой.

2 голосов
/ 27 апреля 2011

Извините, я знаю, что это поздний ответ, но может помочь кому-то.

Еще один потенциально огромный сберегатель памяти - убедиться, что режим отладки Symfony не включен для этой задачи. В пару длительных задач я добавил эту строку, и она сократила использование ОЗУ примерно на 40% даже после того, как я оптимизировал такие вещи, как режим гидратации.

sfConfig::set('sf_debug', false);
1 голос
/ 16 мая 2012

Осторожно с fetchOne () при запросе доктрины. Этот вызов функции не добавит «Предел 1» в SQL

Если вам просто нужно получить одну запись из БД, убедитесь:

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

Использование большого количества памяти на большом столе значительно снижено.

Вы могли видеть, что fetchOne () будет сначала извлекать данные из БД в виде коллекции, а затем возвращать первый элемент.

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;
}
1 голос
/ 07 февраля 2011

У меня была такая же проблема с пакетными заданиями PHP для symfony - если они выполняются в течение длительного времени и используют много данных, которые, как правило, всплывают, и даже если я создал одну оболочку, которая вызывала много отдельных процессов PHP, это не помогло.

Из-за этого я переписал свои большие пакетные задания с помощью Perl DBI, и они надежны и управляемы.

Я не предполагаю, что это лучший ответ, просто сочувствую и предлагаю свой опыт. Вероятно, есть способ заставить PHP вести себя лучше.

0 голосов
/ 30 апреля 2012

Также попробуйте ограничить (выбрать) поля в вашем запросе только теми, которые вам действительно нужны.

например используйте что-то вроде:

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

вместо:

$query = self::createQuery("q")->
  ->select('*')
  ...
0 голосов
/ 07 февраля 2011

Также стоит посмотреть на:

gc_collect_cycles - принудительный сбор любых существующих циклов мусора

...