如何在Symfony 1.4中运行任务时使用更少的内存?

时间:2010-03-17 15:05:44

标签: php memory symfony1 doctrine symfony-1.4

我正在使用Symfony 1.4和Doctrine。

到目前为止,我使用Symfony运行任务没有问题。 但是现在我必须导入相当多的数据并将它们保存在数据库中,我得到了臭名昭着的

  

“致命错误:允许的内存大小   XXXX字节耗尽“

在导入过程中,我只创建新对象,设置几个字段并保存它们。

我很确定这与我在保存数据时创建的对象数量有关。取消设置这些对象不会做任何事情。

在Symfony中是否有限制内存使用的最佳做法?

8 个答案:

答案 0 :(得分:11)

我遇到过这个问题,我发现有几种技术真正有助于Doctrine的大量内存使用。

1:尽可能将Doctrine查询结果保存到数组中。你可以这样做,例如:

$query = self::createQuery("q")->
  ...
  ->setHydrationMode(Doctrine::HYDRATE_ARRAY)
  ->execute();

这会强制Doctrine不创建大对象,而是将其缩减为数组。显然要记住,如果你这样做,就会失去调用方法等的能力,所以只有当你用它来读取字段值等时才会这样做。

2:执行后释放您的结果。这是在Doctrine文档的一个小区域中记录的,但它确实帮助了我正在使用的导入任务:

$query->free();

就是这样。您也可以对已创建的对象执行此操作,例如$myObj->free();,这会强制Doctrine删除它创建的所有循环引用。请注意,在通过PHP范围或unset()删除对象时,循环引用会自动从PHP 5.3开始释放,但在此之前您需要自己完成。

使用它们后取消设置也会有所帮助,虽然这与上面提到的free()方法一起使用,因为unset()不会清除循环引用。

答案 1 :(得分:4)

试试这个:

Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );

所述

php/symfony/doctrine memory leak?

Jordan Feldstein的答案不是我的。

答案 2 :(得分:4)

减少任务内部使用的内存量的另一个提示是禁用查询分析器。大量查询往往会使任务使用越来越多的内存。

为此,请在database.yml配置文件中创建一个新的任务环境,方法是添加以下行:

task:
  doctrine:
    class: sfDoctrineDatabase
    param:
      profiler: false

然后将您的任务设置为在“任务”环境中运行。如果您的查询处于循环中,它应该有助于保持内存使用稳定。

答案 3 :(得分:2)

对不起我知道这是一个迟到的答案,但可以帮助别人。

另一个潜在的巨大内存保护程序是确保没有为该任务启用Symfony的调试模式。在几个长期运行的任务中,我添加了这一行,即使在我对水化模式进行了优化之后,它也减少了大约40%的RAM使用率。

sfConfig::set('sf_debug', false);

答案 4 :(得分:1)

我对symfony的PHP批处理作业有同样的问题 - 如果它们运行很长时间并且使用大量数据它们往往会气球,即使我做了一个调用许多单独的PHP进程的包装器,它没有帮助。

正因为如此,我用Perl的DBI重写了我更大的批处理作业,并且它们可靠且易于管理。

我并不是说这是最好的答案,只是同情和提供我的经验。可能有一种方法可以让PHP表现更好。

答案 5 :(得分:1)

在Doctrine Query上使用fetchOne()时要小心。此函数调用不会在SQL

上追加“限制1”

如果您只需要从DB获取一条记录,请确保:

$q->limit(1)->fetchOne() 

大表上的内存使用率大幅下降。

您可以看到fetchOne()将首先从数据库中提取为集合,然后返回第一个元素。

public function fetchOne($params = array(), $hydrationMode = null)
{
    $collection = $this->execute($params, $hydrationMode);

    if (is_scalar($collection)) {
        return $collection;
    }

    if (count($collection) === 0) {
        return false;
    }

    if ($collection instanceof Doctrine_Collection) {
        return $collection->getFirst();
    } else if (is_array($collection)) {
        return array_shift($collection);
    }

    return false;
}

答案 6 :(得分:0)

另外值得研究:

gc_collect_cycles - 强制收集任何现有的垃圾循环

答案 7 :(得分:0)

还尝试将查询中的(选择)字段限制为您真正需要的字段。

例如使用类似的东西:

$query = self::createQuery("q")->
  ->select('id','title','price')
  ...

而不是:

$query = self::createQuery("q")->
  ->select('*')
  ...