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

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

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

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

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

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

Выше результат в секундах. До и после метода clear(). Тут, собственно, и писать уже почти нечего. Приведу код и пару комментариев.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php

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-ом (что тоже нужно иметь в виду).

Комментарии

avatar
Kompot
avatar
жесть ORM framework на скриптовом языке)))
ответить
avatar
morontt
avatar
Чего же тут такого жестокого? :)
ответить
avatar
Kompot
avatar
где он состояние хранит между вызовами? например маппинг класс-таблица или вычитывает из конфига его заново при каждом call'е?
ответить
avatar
morontt
avatar
Если не используется никакое кеширование, то код будет каждый раз вычитываться и интерпретироваться. К чему твои вопросы? Или ты предлагаешь сайты, например, на ассемблере писать? :)
ответить
avatar
Kompot
avatar
на ассемблере сайты не стоит, дорого получится))) я просто не думал что решения когда заново надо строить маппинг, компилировать запросы и т.д. имеет право на жизнь. а тут оно вон как)
ответить
avatar
morontt
avatar
Различные ORM существуют в разных интерпретируемых языках (PHP, Python, Ruby и т.д.) Ну а раз они существуют, значит это кому-то нужно :-)
ответить
6 комментариев Написать что-нибудь
Адрес электронной почты нигде не отображается, необходим только для обратной связи.