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
  • Вопрос задан
  • 721 просмотр
Решения вопроса 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 нет, от слова совсем.
Ответ написан
Ваш ответ на вопрос

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

Похожие вопросы