Использование памяти при пакетной обработке Doctrine со связями - PullRequest
1 голос
/ 06 апреля 2019

TL; DR: все найденные решения «проблемы производительности импорта Doctrine ORM» не работают, поскольку EntityManager::clear также очищает объекты, необходимые для следующего пакета.

У меня проблема с производительностью, поскольку использование памяти продолжает увеличиваться при выполнении задачи пакетной обработки. Я прочитал много вопросов и постов в блоге об этой проблеме, но все приведенные решения используют очень простой пример вставки сущностей без каких-либо коллекций / отношений. Увеличение лимита памяти «помогло», но по мере роста базы данных у меня даже не хватает физической памяти.

Мой скрипт импорта выглядит примерно так (очень упрощенный пример, заменил мою собственную бизнес-логику некоторыми общими именами):

$customers = $repo->getCustomers(); //array Customer[]
foreach ($customers as $customer) {

    // this line adds about 40kB every time
    $country = $customer->getCity()->getCountry();

    // while this doesn't add
    $city = $customer->getCity();

    // tried to use garbage collector, but even this doesn't free memory
    unset($country);
    $country = null;
    gc_collect_cycles();

    // insert logic removed for the sake of simplicity
}

Когда я добавляю использование EntityManager::clear() после 100 записей (вручную или с использованием Ocramius / DoctrineBatchUtils ), запись # 101 завершится ошибкой, поскольку EntityManager очистил всю информацию, а также информацию, необходимую для выполнения следующего вставки. Поэтому, если я очищаю EntityManager, появляется следующее сообщение об ошибке:

[Doctrine \ ORM \ ORMInvalidArgumentException] Новый объект был найден через отношение «Счет-фактура # клиент», который не был настроен для каскадных операций сохранения для объекта: 740. Для решения этой проблемы: либо явно вызовите EntityManager # persist () в этот неизвестный объект или сконфигурированный каскад сохраняют эту связь в отображении, например, @ManyToOne (.., cascade = {"persist"}).

Я также пытался очистить только некоторые объекты (например, EntityManager::clear(ProductCategory::class)). Я использовал \Doctrine\ORM\EntityManagerInterface::getUnitOfWork, чтобы получить все классы, управляемые Doctrine, и очистка класса либо не влияла на используемую память, либо вызвала сообщение об ошибке выше («найден новый объект ...»).

Как я могу узнать, куда направляется вся моя память и как я могу улучшить свою логику импорта, чтобы предотвратить этот голод памяти?

...