Как использовать меньшую память при выполнении задачи в 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('*')
...