iKatkovJS
@iKatkovJS
Symfony Developer

Как оптимизировать потребление оперативной памяти?

Доброго времени суток!
Есть задача спарсить значения одной таблицы и добавить в другую.
Дана сущность, которая характеризует связь "многое ко многим" между Product и Feature с заполненными product_id и feature_id. В свойство "value" этой сущности нужно добавить значения из другой, ошибочной, сущности.

Вот мною написанный скрипт
private function parseValuesForProductsFeatures()
    {
        $em = $this->getContainer()->get('doctrine')->getManager();
        for ($iteration = 1; $iteration < 1003884; $iteration += 1) {
            $productsFeaturesValue = $em->getRepository('MainCatalogBundle:ProductsFeaturesValues')->findOneBy(array(
                'id' => $iteration,
            ));
            $productId = $productsFeaturesValue->getProduct()->getId();
            $featureId = $productsFeaturesValue->getFeature()->getId();

            $fieldsValue = $em->getRepository('MainCatalogBundle:MistakeProductsFeatures')->findOneBy(array(
                'productId' => $productId,
                'featureId' => $featureId,
            ));

            $value = $fieldsValue->getValue()->getValue();
            $productsFeaturesValue->setValue($value);

            if ($iteration % 200 === 0) {
                $em->flush();
                $em->clear();
            }

        }
        $em->flush();
        $em->clear();
    }


Учитывая, что строк около миллиона оперативной памяти не хватает. Выполнение unset() со всеми переменными в конце каждой 200 итерации не помогает. Куда копать, что решить проблему?
  • Вопрос задан
  • 727 просмотров
Решения вопроса 2
skobkin
@skobkin
Гентушник, разработчик на PHP и Symfony.
Для начала проверьте, что вы выполняете код в среде prod. Среда dev ест значительно больше ресурсов.
А дальше можно перейти на DQL без гидрации в объекты или вовсе на нативные запросы, которые ничего не возвращают в PHP, если сможете оформить запрос с такой логикой.
Ответ написан
Если это разовая операция, то лучше не использовать Doctrine, а сделать все прямым запросом в базу данных. В крайнем случае, можно воспользоваться DBAL, но опять же обычным SQL-запросом.
Если же необходимо все сделать именно средствами Doctrine, то:
  1. Оба репозитория записать в переменные и вынести их перед циклом
  2. Проверить отношения между сущностями, учавствующими в этой операции. Скорее всего, вы не помечаете коллекции как LAZY или EXTRA_LAZY и из-за этого Doctrine тащит кучу ненужных сущностей, забивая память
  3. Если п.2 не помог, то уменьшить кол-во итераций между очистками до 50 - 100
  4. Если п.3 не помог, то увеличить кол-во памяти через ini_set('memory_limit', '512M');
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
nonlux
@nonlux
Это консольное решение?
Зачем все делать нахрапом?1 и1003884 (начало конец цикла) забей как аргумент для cli
выполни задачу меньшими подходами.
1 -1000
1001 -2000
Ответ написан
Комментировать
serhioli
@serhioli
А может добавить магию итераторов?))

<?php
$batchSize = 20;
$i = 0;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
while (($row = $iterableResult->next()) !== false) {
    $em->remove($row[0]);
    if (($i % $batchSize) === 0) {
        $em->flush(); // Executes all deletions.
        $em->clear(); // Detaches all objects from Doctrine!
    }
    ++$i;
}
$em->flush();


Взял отсюда:
latest/reference/batch-processing.html#id1
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы