IgorPI
@IgorPI

Как обработать большое количества данных в Symfony без утечки памяти?

Коллеги, приветствую!

У меня така трабла.
Мне нужно обработать большой объём данных
Порядка 5 000 000 строк в базе
Выполняю привязку к категориям
Задача одноразовая.

Утечка памяти!!

На серваке порядка 48 GB памяти, боюсь и этого не хватит.

Помогите улучшить, сделать течь меньше
class OrganizationToCategory extends Command
{

    /** @var EntityManager */
    private $entityManager;

    /**
     * OrganizationToCategory constructor.
     * @param ObjectManager $manager
     */
    public function __construct(ObjectManager $manager)
    {
        $this->entityManager = $manager;
        $manager->getConnection()->getConfiguration()->setSQLLogger(null);
        parent::__construct();
    }

    /**
     *
     */
    protected function configure()
    {
        $this->setName('app:organization-to-category');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int|void|null
     * @throws ORMException
     * @throws \Doctrine\Common\Persistence\Mapping\MappingException
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {


        $offset = 0;
        while (true) {

            if (!$this->entityManager->isOpen()) {
                $this->entityManager = $this->entityManager->create(
                    $this->entityManager->getConnection(),
                    $this->entityManager->getConfiguration()
                );
            }

            /** @var Organization $organization */
            $organization = $this->getOrganizationOne($offset);

            if (!is_object($organization)) {
                break;
            }

            $category_names = explode("|", $organization->getServicesStr());

            foreach ($category_names as $category_name) {


                $category_repository = $this->entityManager->getRepository("App:Category");

                /** @var ArrayCollection $categories */
                $categories = $category_repository->findBy([
                    "name" => $category_name
                ]);

                if (!is_array($categories)) {
                   continue;
                }

                /** @var Category $category */
                foreach ($categories as $category) {
                    if ($category_name === $category->getName()) {
                        $name = $organization->getName();

                        try {
                            $this->insert($organization->getId(), $category->getId());
                            $output->writeln(sprintf("<info>$name to $category_name memory_get_usage: %d</>", memory_get_usage(true)));
                        }catch (Exception $e) {
                            $output->writeln(sprintf("<error>Привязка уже состоялась!  memory_get_usage: %d</>", memory_get_usage(true)));

                            if (!$this->entityManager->isOpen()) {
                                $this->entityManager = $this->entityManager->create(
                                    $this->entityManager->getConnection(),
                                    $this->entityManager->getConfiguration()
                                );
                            }
                        }
                    }
                }
            }

            foreach ($categories as $category) {
                $this->entityManager->detach($category);
                unset($category);
            }

            $this->entityManager->detach($organization);
            $this->entityManager->clear();
            $this->entityManager->close();

            unset($categories);
            unset($category_names);
            unset($organization);
            unset($category_repository);

            $offset++;
        }


    }


    /**
     * @param int $offset
     * @return mixed
     */
    private function getOrganizationOne($offset = 0)
    {
        /** @var OrganizationRepository $organization_repository */
        $organization_repository = $this->entityManager->getRepository("App:Organization");
        return $organization_repository->get(0,0,0, $offset, 1)[0];
    }


    /**
     * @param int $organization_id
     * @param int $category_id
     * @throws DBALException
     */
    private function insert(int $organization_id, int $category_id)
    {
        $conn = $this->entityManager->getConnection();
        $sql = "
            INSERT INTO `organizations_categories` (`organization_id`, `category_id`) 
            VALUES (:organization_id, :category_id);
        ";

        $stmt = $conn->prepare($sql);
        $stmt->execute([
            "organization_id" => $organization_id,
            "category_id" => $category_id,
        ]);
    }
}


Динамика прироста
ЖЭК № 1 to Коммунальная служба memory_get_usage: 1940389888
Уктс to Коммунальная служба memory_get_usage: 1940389888
ЖЭУ № 7 to Коммунальная служба memory_get_usage: 1940389888
ЖКУ to Коммунальная служба memory_get_usage: 1940389888
Универсал to Коммунальная служба memory_get_usage: 1940389888
КПД to Коммунальная служба memory_get_usage: 1940389888
Пункт Приема Жилищно-Коммунальных Платежей МУП to Коммунальная служба memory_get_usage: 1940389888
Коммунальные платежи to Коммунальная служба memory_get_usage: 1942487040
Группа компаний ЖКХ Сервис to Коммунальная служба memory_get_usage: 1942487040
Группа компаний ЖКХ Сервис to Водосчетчики, газосчетчики, теплосчетчики memory_get_usage: 1942487040
СТ Теплотехник to Коммунальная служба memory_get_usage: 1942487040
Ужэк Домоуправ-НТ to Коммунальная служба memory_get_usage: 1942487040
ЖЭУ № 6 to Коммунальная служба memory_get_usage: 1942487040
Печатный Дом Крым to Широкоформатная печать memory_get_usage: 1942487040
Печатный Дом Крым to Рекламная продукция memory_get_usage: 1942487040
Печатный Дом Крым to Типография memory_get_usage: 1942487040
Типография Консул to Полиграфические услуги memory_get_usage: 1942487040
Типография Консул to Типография memory_get_usage: 1942487040
Константа to Издательские услуги memory_get_usage: 1942487040
Константа to Полиграфические услуги memory_get_usage: 1942487040
Константа to Типография memory_get_usage: 1942487040
  • Вопрос задан
  • 166 просмотров
Решения вопроса 1
VladimirAndreev
@VladimirAndreev
php web dev
1. Используйте batch insets,
2. Выгружайте ряды данных как массивы, а не классы моделей, ну и полей Выгружайте ровно сколько надо.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
index0h
@index0h
PHP, Golang. https://github.com/index0h
Проверьте, включен ли debug режим, очень похоже на то. Для инсертов я бы на вашем месте транзакцию заюзал на пачки в 1к например. 5кк раз пересчитывать индексы - это очень печально. Если категорий у вас не много - имеет смысл их затащить в память все, а дергать мускуль лишний раз не стоит. Смысла в detach при clear / close нет, от слова совсем.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
WEBINAR Москва
от 190 000 руб.
WEBINAR Москва
от 230 000 до 240 000 руб.
от 150 000 до 220 000 руб.
12 дек. 2019, в 23:19
10000 руб./за проект
12 дек. 2019, в 23:04
500000 руб./за проект
12 дек. 2019, в 22:52
1000 руб./за проект