Intereting Posts
Нужно регулярное выражение, чтобы добавить класс css в первый и последний элемент списка JQuery autocomplete ui навигация по страницам со стрелочными клавишами Эффективная ротация баннера с помощью PHP Отключить одно значение параметра внутри тега select, не работающего в IE6Ha Как читать буфер протокола GTFS в PHP? как получить доступ к куки на субдомене Как визуализировать объект DateTime в шаблоне Twig Как добавить пользовательский атрибут в zend framework 2 с помощью zend из Использование mcrypt для передачи данных через веб-службу не выполняется TCPDF ERROR: некоторые данные уже выведены, не удается отправить файл PDF как лучше использовать Smarty с PHP? Ошибка PHP Parse: синтаксическая ошибка, неожиданный T_ENCAPSED_AND_WHITESPACE, ожидающий T_STRING или T_VARIABLE или T_NUM_STRING Форма для представления iframe – как вы можете сбросить форму после загрузки файла? Имя таблицы как параметр с использованием подготовленного оператора PDO / MySQL fopen с / без @ перед этим

Утечка памяти при выполнении запроса Doctrine в цикле

У меня возникли проблемы с поиском причины утечки памяти в моем скрипте. У меня есть простой метод репозитория, который увеличивает столбец «count» в моей сущности на величину X:

public function incrementCount($id, $amount) { $query = $this ->createQueryBuilder('e') ->update('MyEntity', 'e') ->set('e.count', 'e.count + :amount') ->where('e.id = :id') ->setParameter('id', $id) ->setParameter('amount', $amount) ->getQuery(); $query->execute(); } 

Проблема в том, что если я назову это в цикле всплесками использования памяти на каждой итерации:

 $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); $myRepository = $doctrineManager->getRepository('MyEntity'); while (true) { $myRepository->incrementCount("123", 5); $doctrineManager->clear(); gc_collect_cycles(); } 

Что мне здесь не хватает? Я пробовал ->clear() , согласно совету Doctrine по пакетной обработке . Я даже пробовал gc_collect_cycles() , но проблема остается.

Я запускаю Doctrine 2.4.6 на PHP 5.5.

Я решил это, добавив --no-debug к моей команде. Оказывается, в режиме отладки профилировщик хранил информацию о каждом запросе в памяти.

Вы теряете память для каждой итерации. Гораздо лучший способ – подготовить запрос один раз и заменить аргументы много раз . Например:

 class MyEntity extends EntityRepository{ private $updateQuery = NULL; public function incrementCount($id, $ammount) { if ( $this->updateQuery == NULL ){ $this->updateQuery = $this->createQueryBuilder('e') ->update('MyEntity', 'e') ->set('e.count', 'e.count + :amount') ->where('e.id = :id') ->getQuery(); } $this->updateQuery->setParameter('id', $id) ->setParameter('amount', $amount); ->execute(); } } 

Как вы упомянули, вы можете использовать пакетную обработку здесь, но сначала попробуйте это и посмотрите, насколько хорошо (если вообще) выполняет …

Я просто столкнулся с тем же вопросом, это то, что исправило это для меня:

–no-отладка

Как указывает OP в своем ответе, установка --no-debug (ex: php app/console <my_command> --no-debug ) имеет решающее значение для производительности / памяти в командах консоли Symfony. Это особенно верно при использовании Doctrine, так как без него Doctrine переходит в режим отладки, который потребляет огромное количество дополнительной памяти (что увеличивается на каждой итерации). Дополнительную информацию см. В документах Symfony здесь и здесь .

–env = прод

Вы также должны всегда указывать среду. По умолчанию Symfony использует среду dev для консольных команд. Обычно среда dev не оптимизирована для памяти, скорости, процессора и т. Д. Если вы хотите перебирать тысячи элементов, вероятно, вы должны использовать среду prod (например: php app/console <my_command> --no-debug ) , См. Здесь и здесь для получения дополнительной информации.

Совет. Я создал среду под названием console которую я специально настроил для запуска консольных команд. Вот информация о том, как создавать дополнительные среды Symfony .

php -d memory_limit = YOUR_LIMIT

Если вы используете большое обновление, вам, вероятно, следует выбрать, сколько памяти приемлемо для его потребления. Это особенно важно, если вы считаете, что может произойти утечка. Вы можете указать память для команды, используя php -d memory_limit=x (ex: php -d memory_limit=256M ). Примечание: вы можете установить ограничение на -1 (обычно по умолчанию для php cli), чтобы команда запускалась без ограничения памяти, но это, очевидно, опасно.

Хорошо сформированная консольная команда для пакетной обработки

Хорошо сформированная консольная команда для запуска большого обновления с использованием приведенных выше советов будет выглядеть так:

php -d memory_limit=256M app/console <acme>:<your_command> --env=prod --no-debug

Использовать IterableResult от Doctrine

Еще один огромный, когда используется ORM в Doctrine в цикле, заключается в использовании доктрины IterableResult (см. Документы обработки пакетной обработки Doctrine ). Это не поможет в приведенном примере, но обычно при выполнении такой обработки это результат запроса.

Выводите использование памяти во время вашей работы.

Может быть очень полезно отслеживать, сколько памяти потребляет ваша команда во время ее работы. Это можно сделать, выведя ответ, возвращаемый встроенной функцией памяти_get_usage () PHP.

Удачи!

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

Чтобы преодолеть это, вам нужно отключить Doctrine SQL Logger.

Я рекомендую делать это только для части цикла.

Перед циклом получить текущий регистратор:

$sqlLogger = $em->getConnection()->getConfiguration()->getSQLLogger();

А затем отключите SQL Logger:

$ Em-> GetConnection () -> getConfiguration () -> setSQLLogger (нуль);

Делайте петлю здесь: foreach() / while() / for()

После окончания цикла верните Logger:

$em->getConnection()->getConfiguration()->setSQLLogger($sqlLogger);

Для меня это очищало доктрину или, как говорится в документации, отделяя все сущности:

 $this->em->clear(); //Here em is the entity manager. 

Таким образом, внутри моей петли y сбрасывает каждые 1000 итераций и отделяет все сущности (мне они больше не нужны):

  foreach ($reader->getRecords() as $position => $value) { $this->processValue($value, $position); if($position % 1000 === 0){ $this->em->flush(); $this->em->clear(); } $this->progress->advance(); } -  foreach ($reader->getRecords() as $position => $value) { $this->processValue($value, $position); if($position % 1000 === 0){ $this->em->flush(); $this->em->clear(); } $this->progress->advance(); } 

Надеюсь это поможет.

PS: вот документация .