Doctrine 2 → Падение производительности при импорте данных
Не в первом проекте уже сталкиваюсь с ситуацией, когда при импорте данных при помощи ORM Doctrine 2 происходит падение производительности с увеличением количества импортируемых данных. Вот и сейчас проявилась данная особенность, а заодно и обнаружилось решение, которым спешу поделиться :)
Небольшое вступление. Переписываю один сайт с некой CMS на более-менее человеческий фреймворк, а заодно разбираю ту кашу, которая образовалась внутри базы данных (из-за того, что прежним разработчикам неоднократно приходилось прикручивать функционал, на который CMS-ка, естественно, не была рассчитана изначально). Пишу определённые SQL-запросы, которые вытягивают информацию в нужной мне форме из старой БД и складывают её в CSV-файлы, из которых впоследствии эти же данные переносятся доктриной в новую БД.
На последнем этапе как раз и наблюдается неприятное явление. Первые порции данных в цикле импортируются быстро, а потом становится заметно, что скорость всё падает и падает.
Вывешу скриншот из серии БЫЛО и СТАЛО, остальное под катом...
Выше результат в секундах. До и после метода clear(). Тут, собственно, и писать уже почти нечего. Приведу код и пару комментариев.
namespace Hypersoft\UltraBundle\Services; use Doctrine\Bundle\DoctrineBundle\Registry; use Symfony\Component\Console\Output\OutputInterface; use Hypersoft\UltraBundle\Entity\Tag; //bla-bla-bla class CsvReader { protected $doctrine; protected $em; /** * @param Registry $doctrine */ public function __construct(Registry $doctrine) { $this->doctrine = $doctrine; $this->em = $doctrine->getManager(); $this->em->getConfiguration() ->setSQLLogger(null); //bla-bla-bla } //bla-bla-bla /** * @param string $filename * @param \Symfony\Component\Console\Output\OutputInterface $output * @return array */ public function importTags($filename, OutputInterface $output) { $dataArray = $this->readFile($filename); //bla-bla-bla foreach ($dataArray as $item) { $tag = new Tag(); //bla-bla-bla $this->em->persist($tag); $this->em->flush(); $output->writeln('insert tag ID = ' . $tag->getId()); $this->em->clear(); } return $dataArray; } }
Самое интересно происходит в строках, помеченных более светлым фоном. Это строки 22-23, где происходит отключение логгера и строка 52, которая и позволила ускориться.
Выключение SQL-логгера не даёт сколько-нибудь заметного прироста производительности, зато уменьшает потребление памяти. А метод clear вычищает UnitOfWork от всевозможной подноготной требухи, собираемой доктриной в процессе работы, в частности, будут отсоединяться объекты, управляемые на данный момент EntityManager-ом (что тоже нужно иметь в виду).
Комментарии