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

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

Моя модель имеет определенный объект, называемый "сектор", каждый из которых имеет несколько объектов типа "Cupo" (обычно от 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 терпит неудачу. Спасибо за помощь.

Ответ 1

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

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

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

Update:

Я создал новую среду в моих базах данных .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;

to

unset($cupo);

И теперь мой script с радостью отбивается. Я уверен, что на этот раз он закончит, не закончив RAM.

Обновление 3

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

Ответ 2

Я только что сделал "daemonized" script с symfony 1.4 и установил следующее, остановив память:

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

Ответ 3

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

  • Отключить режим отладки. Добавьте следующее, чтобы соединение db инициализировалось

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

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

    $object_name->free()
    
  • Отключите все массивы после использования unset($array_name)

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

$cupo->save();

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

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

Ответ 7

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

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

(С PROD CONFIG)
и используйте free() после сохранения() объекта доктрины

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

memory_get_usage=26.884071350098Mo

с php 5.3 на debian squeeze

Ответ 8

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

Ответ 9

То, что работает для меня, вызывает метод free следующим образом:

$cupo->save();
$cupo->free(true); // free also the related components
unset($cupo);