2010-01-19 4 views
16

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

В моей модели есть определенный объект под названием «Сектор», каждый из которых имеет несколько объектов типа «Купо» (обычно от 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

отметьте чей-то ответ как правильный, а? – develop7

+0

Для массовых вставок вам также может потребоваться сделать необработанную SQL-вставку через PDO, это определенно будет быстрее, и это не приведет к утечке памяти. –

ответ

1

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

1

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

$cupo->save(); 

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

в described в Doctrine руководстве.

0

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

3

Я просто сделал "daemonized" сценарий с Symfony 1.4 и настройки следующих остановил память коробления:

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

Это сработало для меня, если вы сделаете это, прежде чем создавать менеджера баз данных. Если у вас есть задача, я думаю, что это безопасно. –

33

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

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

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

Update:

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

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

профилировщика: ложная запись отключает ведение журнала запросов Доктрины в, который обычно держит копия каждого сделанного вами запроса. Это не остановило утечку памяти, но мне удалось получить вдвое больше, чем импортировать данные, поскольку я был без нее.

Update 2

Я добавил

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

перед запуском мои запросы, и изменил

$cupo = null; 

в

unset($cupo); 

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

Update 3

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

+1

У меня такая же проблема. Я пробовал эти вещи, и моя производительность значительно улучшилась. –

+0

Если возможно, обновите до php5.3 и используйте -> free(). Утечки памяти вызваны сборщиком мусора PHP5.2, который не может очистить круговые ссылки, которые больше не ссылаются из-за пределов их цикла. PHP5.3 имеет лучший сборщик, и free() пытается отменить эти ссылки до того, как переменная покинет область видимости. –

+1

@Jordan - WOW У меня была аналогичная проблема - но на выбор - профайлер: false имел HUGE impact ... спасибо – ManseUK

2

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

+0

Я сделал тот же опыт, что все эти маленькие трюки действительно не помогают. Я изложил подобный подход к вашему: http://stackoverflow.com/a/11474869/620410 – Tapper

0

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

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

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

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

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

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

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

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

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

1

Для меня, я просто инициализируется задачу так:

// 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)
и использовать бесплатно() после сохранения() на объекте доктрины в

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

memory_get_usage=26.884071350098Mo 

с PHP 5.3 на DEBiAN отжимают