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

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

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

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

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

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

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

Ответ 1

Я столкнулся с этим, и там несколько методов, которые я нашел, действительно помогли с использованием 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() не очистит обратные ссылки в противном случае.

Ответ 2

Попробуйте следующее:

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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

вместо:

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