утечка памяти в php / symfony / doctrine? - PullRequest
16 голосов
/ 20 января 2010

У меня проблемы с пакетной вставкой объектов в базу данных с использованием Symfony 1.4 и Doctrine 1.2.

В моей модели есть объект определенного типа, называемый «Сектор», каждый из которых имеет несколько объектов типа «Купо» (обычно в диапазоне от 50 до 200000). Эти объекты довольно маленькие; просто короткая строка идентификатора и одно или два целых числа. Всякий раз, когда пользователь создает группу секторов, мне нужно автоматически добавлять все эти экземпляры «Cupo» в базу данных. На случай, если что-то пойдет не так, я использую транзакцию доктрины для отката всего. Проблема в том, что я могу создать только около 2000 экземпляров, прежде чем php не хватит памяти. В настоящее время он имеет ограничение в 128 МБ, которого должно быть более чем достаточно для обработки объектов, использующих менее 100 байтов. Я пытался увеличить лимит памяти до 512 МБ, но php все еще падает, и это не решает проблему. Правильно ли выполняется пакетная вставка или есть способ получше?

Вот ошибка:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 71 bytes) in /Users/yo/Sites/grifoo/lib/vendor/symfony/lib/log/sfVarLogger.class.php on line 170

А вот код:

public function save($conn=null){

    $conn=$conn?$conn:Doctrine_Manager::connection();

    $conn->beginTransaction();


    try {
        $evento=$this->object;


        foreach($evento->getSectores() as $s){

            for($j=0;$j<$s->getCapacity();$j++){

                $cupo=new Cupo();
                $cupo->setActivo($s->getActivo());
                $cupo->setEventoId($s->getEventoId());
                $cupo->setNombre($j);
                $cupo->setSector($s);

                $cupo->save();

            }
        }

        $conn->commit();
        return;
    }
    catch (Exception $e) {
        $conn->rollback();
        throw $e;
    }

Еще раз, этот код отлично работает для менее чем 1000 объектов, но все, что больше 1500, терпит неудачу. Спасибо за помощь.

Ответы [ 9 ]

33 голосов
/ 01 ноября 2010

Пробовал делать

$cupo->save();
$cupo->free();
$cupo = null;

(но заменяя мой код) И я все еще получаю переполнение памяти.Любые другие идеи, SO?

Обновление:

Я создал новую среду в моем database.yml, которая выглядит так:

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn: 'mysql:host=localhost;dbname=.......'
      username: .....
      password: .....
      profiler: false

Запись profiler: false отключает ведение журнала запросов доктрины, в котором обычно хранится копия каждого сделанного вами запроса.Это не остановило утечку памяти, но я смог получить вдвое больший объем импорта данных, чем без него.

Обновление 2

Я добавил

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

перед выполнением моих запросов и изменил

$cupo = null;

на

unset($cupo);

И теперь мой сценарий счастливо отрабатывается.Я почти уверен, что на этот раз он закончится без использования ОЗУ.

Обновление 3

Да.Это выигрышная комбинация.

3 голосов
/ 01 ноября 2011

Для задачи Symfony я также столкнулся с этой проблемой и сделал следующие вещи.Это сработало для меня.

  • Отключить режим отладки.Добавьте следующее перед инициализацией соединения с БД

    sfConfig::set('sf_debug', false);
    
  • Установите свободный атрибут объекта автоматического запроса для соединения с БД

    $connection->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
    
  • Освободите весь объект после использования

    $object_name->free()
    
  • Сброс всех массивов после использования unset($array_name)

  • Проверка всех запросов доктрины, используемых в задаче.Бесплатные все запросы после использования.$q->free() (Это хорошая практика для любого времени использования запроса.)

Вот и все.Надеюсь, это может кому-то помочь.

3 голосов
/ 20 апреля 2010

Я только что сделал «демонизированный» скрипт с symfony 1.4, и установка следующего остановила захват памяти:

2 голосов
/ 13 ноября 2010

Утечка доктрины, и вы ничего не можете с этим поделать.Убедитесь, что вы используете $ q-> free (), когда это применимо, чтобы минимизировать эффект.Доктрина не предназначена для сценариев обслуживания.Единственный способ обойти эту проблему - разбить ваш скрипт на части, которые будут выполнять часть задачи.Один из способов сделать это - добавить параметр запуска к вашему сценарию, и после того, как определенное количество объектов будет обработано, сценарий перенаправляется на себя с более высоким значением начала.Это хорошо работает для меня, хотя и делает написание сценариев обслуживания более обременительным.

1 голос
/ 04 июля 2012

Для меня, я только что инициализировал задачу следующим образом:

// initialize the database connection
$databaseManager = new sfDatabaseManager($this->configuration);
$connection = $databaseManager->getDatabase($options['connection'])->getConnection();
$config = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', true);
sfContext::createInstance($config);

(С КОНФИГОМ ПРОД)и использовать free () после save () для объекта доктрины

память стабильна при 25Mo

memory_get_usage=26.884071350098Mo

с php 5.3 на debian squeeze

1 голос
/ 20 января 2010

Попробуйте разбить круговую ссылку, которая обычно вызывает утечки памяти, с помощью

$cupo->save();

$cupo->free(); //this call

как , описанного в руководстве по доктрине.

1 голос
/ 20 января 2010

Попробуйте unset($cupo); после каждого сохранения. Это должно помочь. Другое дело - разбить скрипт и выполнить некоторую пакетную обработку.

0 голосов
/ 09 февраля 2011

Что работает для меня, так это вызов метода free:

$cupo->save();
$cupo->free(true); // free also the related components
unset($cupo);
0 голосов
/ 07 апреля 2010

Периодически закрывайте и снова открывайте соединение - не знаю почему, но, похоже, PDO сохраняет ссылки.

...