Подтвердить что ты не робот

Doctrine2 - Как повысить эффективность флеша?

Мне нужно обновить сущности Doctrine, чтобы соответствовать записям внутри файла (potentionaly very large) XML. Я также должен обновлять ассоциации ManyToMany в соответствии с данными в XML. Это то, что я делаю внутри цикла:

  • получить данные из XML
  • получить сущность из БД (если не существует, создать новый)
  • установить новые свойства объекта
  • получить текущие ассоциации сущностей (getter возвращает ArrayCollection объект)
  • очистить все ассоциации (вызывая ArrayCollection::clear())
  • установить новые ассоциации (вызывая ArrayCollection::add() в подцикле)
  • сохранить объект EntityManager

После цикла я вызываю EntityManager::flush().

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

  • SELECT для получения сущности из базы данных
  • UPDATE для обновления свойств объекта (это фактически пропущено сейчас, поскольку свойства не изменены... еще)
  • УДАЛИТЬ, чтобы очистить предыдущие ассоциации.
  • INSERT для вставки новых ассоциаций

Итак, в общей сложности для 305 записей в XML я получаю 915 запросов (я думаю, он мог бы доходить до 1220 запросов, если бы все сущности были изменены), что делает импорт очень медленным.

Я мог бы использовать IdentityMap и предварительную выборку сущностей до цикла, но все еще есть запросы UPDATE/DELETE/INSERT.

  • Есть ли способ, чтобы метод flush лучше оптимизировал запросы (используйте multi-insert, WHERE IN вместо нескольких запросов DELETE и т.д.)?
  • Это обычное поведение метода flush или я делаю что-то неправильно?
  • Возможно, есть проблема в том, как я обновляю ассоциации сущности. Есть ли лучший способ, как это сделать? (вместо метода get/clear/add)
  • Я знаю, что Doctrine не предназначен для массовой обработки betch, но я думаю, что использование этого для импорта XML - лучший способ избежать несоответствий БД, которые могут появиться с использованием подхода без ORM. Это правильно?
  • Если описанный выше подход неверен, как мне решить проблему?
4b9b3361

Ответ 1

Вы делаете это правильно - это просто медленно, потому что добавленная абстракция ORM означает, что вы не можете сделать нужные вам оптимизации.

Тем не менее, EntityManager делает медленные транзакции, большие. Если вам не нужны все они в одной крупной транзакции, вы можете получить более эффективный код с помощью flush() ing, а затем очистить() от EM каждые 20-200 итераций вашего цикла.

Если это не дает вам достаточной производительности, единственной альтернативой, о которой я могу думать, является возврат к настраиваемому коду, который запускает собственный SQL непосредственно против вашей СУБД.

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

------ edit ------

Из официальной статьи Doctrine2 на Пакетная обработка:

Некоторые люди, похоже, задаются вопросом, почему Doctrine не использует multi-inserts (вставлять в (...) значения (...), (...), (...),...

Прежде всего, этот синтаксис поддерживается только для mysql и newer postgresql версии. Во-вторых, нет простого способа овладеть всеми сгенерированные идентификаторы в такой мульти-вставке при использовании AUTO_INCREMENT или SERIAL и ORM нужны идентификаторы для идентификации управление объектами. Наконец, производительность вставки редко узкое место ORM. Нормальные вставки более чем достаточно быстро для в большинстве ситуаций, и если вы действительно хотите делать быстрые объемные вставки, multi-insert - это не лучший способ, т.е. Postgres COPY или Mysql LOAD DATA INFILE на несколько порядков быстрее.

Вот причины, по которым не стоит абстракция, которая выполняет множественные вставки на mysql и postgresql в ОРМ.

Также существует значительная разница в производительности при использовании базы данных удаленная и локальная, поскольку накладные расходы на отправку каждого запроса на удаленный сервер довольно велики. Накладные расходы намного ниже при использовании локальной базы данных благодаря транзакциям и оптимизации БД. (например, на 70 с ниже до 300 мс в случае примера в вопросе)

Ответ 2

Не уверен, что это напрямую ответит на вопрос, заданный исходным плакатом, но, надеюсь, это поможет другим с проблемами скорости Doctrine при промывке.

... Что касается скорости очистки, убедитесь, что ваш профайлер 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 секунд!