morontt.info

Doctrine 2 → Падение производительности при импорте данных

Не в первом проекте уже сталкиваюсь с ситуацией, когда при импорте данных при помощи ORM Doctrine 2 происходит падение производительности с увеличением количества импортируемых данных. Вот и сейчас проявилась данная особенность, а заодно и обнаружилось решение, которым спешу поделиться :)

Небольшое вступление. Переписываю один сайт с некой CMS на более-менее человеческий фреймворк, а заодно разбираю ту кашу, которая образовалась внутри базы данных (из-за того, что прежним разработчикам неоднократно приходилось прикручивать функционал, на который CMS-ка, естественно, не была рассчитана изначально). Пишу определённые SQL-запросы, которые вытягивают информацию в нужной мне форме из старой БД и складывают её в CSV-файлы, из которых впоследствии эти же данные переносятся доктриной в новую БД.

На последнем этапе как раз и наблюдается неприятное явление. Первые порции данных в цикле импортируются быстро, а потом становится заметно, что скорость всё падает и падает.

Вывешу скриншот из серии БЫЛО и СТАЛО, остальное под катом...

Результат метода clear

Выше результат в секундах. До и после метода 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-ом (что тоже нужно иметь в виду).

g-plus-icon
comments powered by Disqus
Учтите, что комментарии добавляются только при включённом JavaScript в браузере. В ином случае информация хоть и не пропадёт бесследно, но будет отправлена в спам и вряд ли хоть кто-нибудь её увидит.
Kompot
avatar
жесть ORM framework на скриптовом языке)))
Ответить
morontt
avatar
Чего же тут такого жестокого? :)
Ответить
Kompot
avatar
где он состояние хранит между вызовами? например маппинг класс-таблица или вычитывает из конфига его заново при каждом call'е?
Ответить
morontt
avatar
Если не используется никакое кеширование, то код будет каждый раз вычитываться и интерпретироваться. К чему твои вопросы? Или ты предлагаешь сайты, например, на ассемблере писать? :)
Ответить
Kompot
avatar
на ассемблере сайты не стоит, дорого получится))) я просто не думал что решения когда заново надо строить маппинг, компилировать запросы и т.д. имеет право на жизнь. а тут оно вон как)
Ответить
morontt
avatar
Различные ORM существуют в разных интерпретируемых языках (PHP, Python, Ruby и т.д.) Ну а раз они существуют, значит это кому-то нужно :-)
Ответить
6 комментариев Написать что-нибудь
Поля, помеченные asterisk, обязательны для заполнения.
Адрес электронной почты нигде не отображается, необходим только для обратной связи.
Веб-сайт вводите в формате http://example.org, при желании, конечно.