У меня возникают проблемы с пакетной вставкой объектов в базу данных с использованием 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 терпит неудачу. Спасибо за помощь.
Пробовал делать
$cupo->save(); $cupo->free(); $cupo = null;
(Но подставляя мой код) И я все еще получаю переполнение памяти. Любые другие идеи, ТАК?
Обновить:
Я создал новую среду в моих базах данных.yml, которая выглядит так:
all: doctrine: class: sfDoctrineDatabase param: dsn: 'mysql:host=localhost;dbname=.......' username: ..... password: ..... profiler: false
Профайлер: ложная запись отключает ведение журнала запросов, которая обычно хранит копию каждого сделанного вами запроса. Это не остановило утечку памяти, но мне удалось получить вдвое больше, чем импортировать данные, поскольку я был без нее.
Обновление 2
я добавил
Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
перед запуском моих запросов и изменением
$cupo = null;
в
unset($cupo);
И теперь мой сценарий с радостью отбивается. Я уверен, что на этот раз он закончит, не закончив RAM.
Обновление 3
Ага. Это выигрышная комбинация.
Я только что «демоннизировал» скрипт с symfony 1.4 и установил следующее:
sfConfig::set('sf_debug', false);
Для задачи 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()
(Это хорошая практика для любого использования запроса). Это все. Надеюсь, это может помочь кому-то.
Утечка просачивается, и вы не можете с этим поделать. Убедитесь, что вы используете $ q-> free (), когда это применимо, чтобы минимизировать эффект. Доктрина не предназначена для сценариев обслуживания. Единственный способ обойти эту проблему – разбить скрипт на части, которые будут выполнять часть задачи. Один из способов сделать это – добавить начальный параметр к вашему сценарию и после того, как определенное количество объектов было обработано, сценарий перенаправляет себя с более высоким стартовым значением. Это хорошо работает для меня, хотя это делает письменные сценарии обслуживания более громоздкими.
Попробуйте unset($cupo);
после каждой экономии. Это должна быть помощь. Другое дело – разбить скрипт и выполнить пакетную обработку.
Попробуйте разбить круговую ссылку, которая обычно вызывает утечку памяти с помощью
$cupo->save(); $cupo->free(); //this call
как описано в руководстве Doctrine.
Для меня я только что инициализировал задачу:
// 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
Периодически закрывайте и повторно открывайте соединение – не уверен, почему, но, похоже, PDO сохраняет ссылки.
То, что работает для меня, вызывает free
метод следующим образом:
$cupo->save(); $cupo->free(true); // free also the related components unset($cupo);