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

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

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

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

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

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

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

8 ответов

Решение

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

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

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

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

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

$query->free();

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

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

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

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

как упоминалось на

утечка памяти в php/symfony/doctrine?

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

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

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

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

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

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

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

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

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

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

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

Осторожно при использовании fetchOne() в Doctrine Query. Этот вызов функции не добавит "Предел 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;
}

Также стоит изучить:

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

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

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

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

вместо:

$query = self::createQuery("q")->
  ->select('*')
  ...
Другие вопросы по тегам