在循环中执行Doctrine查询时内存泄漏

php symfony memory-leaks doctrine-orm doctrine

4659 观看

5回复

5745 作者的声誉

我在查找脚本中的内存泄漏原因时遇到了麻烦。我有一个简单的存储库方法,它将我的实体中的'count'列递增X量:

public function incrementCount($id, $amount)
{
    $query = $this
        ->createQueryBuilder('e')
        ->update('MyEntity', 'e')
        ->set('e.count', 'e.count + :amount')
        ->where('e.id = :id')
        ->setParameter('id', $id)
        ->setParameter('amount', $amount)
        ->getQuery();

    $query->execute();
}

问题是,如果我在循环中调用它,则每次迭代都会占用内存使用量:

$doctrineManager = $this->getContainer()->get('doctrine')->getManager();
$myRepository = $doctrineManager->getRepository('MyEntity');
while (true) {
    $myRepository->incrementCount("123", 5);
    $doctrineManager->clear();
    gc_collect_cycles();
}

我在这里错过了什么?->clear()根据Doctrine 关于批处理建议,我试过了。我甚至尝试过gc_collect_cycles(),但问题仍然存在。

我在PHP 5.5上运行Doctrine 2.4.6。

作者: Jonathan 的来源 发布者: 2014 年 10 月 28 日

回应 (5)


4

16943 作者的声誉

你每次迭代都在浪费内存。更好的方法是准备一次查询并多次交换参数。例如:

class MyEntity extends EntityRepository{
    private $updateQuery = NULL;

    public function incrementCount($id, $ammount)
    {
        if ( $this->updateQuery == NULL ){
            $this->updateQuery = $this->createQueryBuilder('e')
                ->update('MyEntity', 'e')
                ->set('e.count', 'e.count + :amount')
                ->where('e.id = :id')
                ->getQuery();
        }

        $this->updateQuery->setParameter('id', $id)
                ->setParameter('amount', $amount);
                ->execute();
    }
}

正如您所提到的,您可以在这里使用批处理,但首先尝试一下,看看有多好(如果有的话)......

作者: Jovan Perovic 发布者: 28.10.2014 07:42

13

5745 作者的声誉

决定

我通过添加--no-debug到我的命令解决了这个问题。事实证明,在调试模式下,探查器正在存储有关每个查询的内存信息。

作者: Jonathan 发布者: 05.11.2014 11:46

4

392 作者的声誉

我刚遇到同样的问题,这些是为我解决的问题:

--no调试

正如他们在答案中提到的那样,设置--no-debug(ex php app/console <my_command> --no-debug:)对于Symfony控制台命令中的性能/内存至关重要。使用Doctrine时尤其如此,因为没有Doctrine,Doctrine将进入调试模式,这会消耗大量的额外内存(每次迭代都会增加)。有关详细信息,请参阅此处此处的Symfony文档。

--env = PROD

您还应始终指定环境。默认情况下,Symfony将dev环境用于控制台命令。该dev环境通常如果要迭代上千项没有内存,速度,CPU等进行了优化,你或许应该使用的prod环境(例如:php app/console <my_command> --no-debug)。有关详细信息,请参阅此处此处

提示:我创建了一个名为“ console我专门为运行控制台命令而配置” 的环境。以下是有关如何创建其他Symfony环境的信息

php -d memory_limit = YOUR_LIMIT

如果运行大更新,您应该选择可以使用的内存量。如果您认为可能存在泄漏,这一点尤为重要。您可以使用php -d memory_limit=x(ex :) 指定Command的内存php -d memory_limit=256M。注意:您可以将限制设置为-1(通常是php cli的默认值),让命令在没有内存限制的情况下运行,但这显然很危险。

用于批处理的良好形成的控制台命令

使用上述提示运行大更新的格式良好的控制台命令如下所示:

php -d memory_limit=256M app/console <acme>:<your_command> --env=prod --no-debug

使用Doctrine的IterableResult

在循环中使用Doctrine的ORM时,另一个巨大的问题是使用doctrine的IterableResult(参见Doctrine Batch Processing文档)。这在提供的示例中没有帮助,但通常在进行这样的处理时,它会超过查询的结果。

随时输出内存使用情况。

跟踪命令在运行时消耗的内存量非常有用。您可以通过输出PHP内置的memory_get_usage()函数返回的响应来实现。

祝好运!

作者: Collin Krawll 发布者: 19.11.2016 11:01

4

151 作者的声誉

Doctrine记录您所做的任何查询。如果你进行大量查询(通常在循环中发生),Doctrine会导致巨大的内存泄漏。

您需要禁用Doctrine SQL Logger来克服这个问题。

我建议仅对循环部分执行此操作。

在循环之前,获取当前记录器:

$sqlLogger = $em->getConnection()->getConfiguration()->getSQLLogger();

然后禁用SQL Logger:

$ EM->的getConnection() - > getConfiguration() - > setSQLLogger(空);

在这里循环: foreach() / while() / for()

循环结束后,放回Logger:

$em->getConnection()->getConfiguration()->setSQLLogger($sqlLogger);

作者: mFlorin 发布者: 11.05.2017 12:57

3

156 作者的声誉

对我来说,它是清除原则,或者正如文档所说,分离所有实体:

$this->em->clear(); //Here em is the entity manager.

所以在我的循环中,每1000次迭代冲洗并分离所有实体(我不再需要它们):

    foreach ($reader->getRecords() as $position => $value) {
        $this->processValue($value, $position);
        if($position % 1000 === 0){
            $this->em->flush();
            $this->em->clear();
        }
        $this->progress->advance();
    }

希望这可以帮助。

PS:这是文档

作者: amcastror 发布者: 25.11.2017 05:27
32x32