Я должен обновить сущности Doctrine, чтобы соответствовать записям внутри файла (potentionaly very large) XML. Я также должен обновлять ассоциации ManyToMany в соответствии с данными в XML. Это то, что я делаю внутри цикла:
ArrayCollection
) ArrayCollection::clear()
) ArrayCollection::add()
в ArrayCollection::add()
) После цикла я вызываю EntityManager::flush()
.
Проблема заключается в том, что очистка генерирует большое количество запросов вместо обновления / вставки / удаления нескольких строк одновременно. Для каждого объекта выполняются следующие запросы:
Так что всего за 305 записей в XML я получаю 915 запросов (я думаю, он мог бы доходить до 1220 запросов, если бы все сущности были изменены), что делает импорт очень медленным.
Я мог бы воспользоваться преимуществами IdentityMap и предварительной выборки сущностей до цикла, но все еще есть запросы UPDATE / DELETE / INSERT.
Вы делаете это правильно – это просто медленно, потому что добавленная абстракция ORM означает, что вы не можете сделать нужные вам оптимизации.
Тем не менее, EntityManager не спешит с транзакциями, большими. Если вам не нужны все они в одной крупной транзакции, вы можете получить более эффективный код с помощью flush () ing, а затем очистить () от EM каждые 20-200 итераций вашего цикла.
Если это не дает вам достаточной производительности, единственной альтернативой, о которой я могу думать, является возврат к пользовательскому коду, который запускает собственный SQL непосредственно против вашей СУБД.
Я знаю, что это не отличный ответ, но, по крайней мере, я могу сказать вам, что вы не сумасшедший.
—— редактировать ——
Из официальной статьи Doctrine2 о пакетной обработке :
Некоторые люди, похоже, задаются вопросом, почему Doctrine не использует мульти-вставки (вставлять в (…) значения (…), (…), (…), …
Прежде всего, этот синтаксис поддерживается только в mysql и более поздних версиях postgresql. Во-вторых, нет простого способа получить все сгенерированные идентификаторы в такой мультиинсталляции при использовании AUTO_INCREMENT или SERIAL, а ORM нужны идентификаторы для управления идентификацией объектов. Наконец, вставка производительности редко является узким местом ORM. Обычные вставки более чем достаточно быстры для большинства ситуаций, и если вы действительно хотите делать быстрые вставки вставки, то мульти-вставка не является наилучшим способом, то есть Postgres COPY или Mysql LOAD DATA INFILE на несколько порядков быстрее.
Это причины, по которым не стоит пытаться реализовать абстракцию, которая выполняет множественные вставки в mysql и postgresql в ORM.
Также существует значительная разница в производительности при использовании удаленной vs локальной базы данных, поскольку накладные расходы на отправку каждого запроса на удаленный сервер довольно велики. Накладные расходы намного ниже при использовании локальной базы данных благодаря транзакциям и оптимизации БД. (например, в случае примера в вопросе на 70 с ниже до 300 мс)
Не уверен, что это напрямую ответит на вопрос, заданный исходным плакатом, но, надеюсь, это поможет другим с проблемами скорости доктрины при промывке.
… Что касается скорости очистки, убедитесь, что ваш профайлер xdebug не включен.
[php.ini] ; PROFILING ;xdebug.profiler_enable = 1 ;xdebug.profiler_output_name = "cachegrind.out.%t.%s.%p" ;xdebug.profiler_output_dir = "C:\xampp\tmp"
В качестве примера того, насколько это повлияло на операцию по очистке Doctrine в моем случае, это было 55 секунд для 3000 записей, тогда как профайлер выключен, это было 5 секунд!