17

我正在使用 Symfony 1.4 和 Doctrine。

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

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

在此导入期间,我只创建新对象、设置一些字段并保存它们。

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

是否有任何最佳实践来限制 Symfony 中的内存使用?

4

8 回答 8

11

我遇到过这个问题,我发现有一些技术确实有助于 Doctrine 的大量内存使用。

1:在可能的情况下,将 Doctrine 查询结果合并为一个数组。您可以按如下方式执行此操作,例如:

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

这迫使 Doctrine 不创建大对象,而是将其简化为数组。显然请记住,如果你这样做,你将失去调用方法等的能力,所以这只有在你使用它来读取字段值等时才有用。

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

$query->free();

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

使用它们后取消设置变量也有帮助,尽管与上述free()方法结合使用,unset()否则不会清除循环引用。

于 2010-03-17T15:13:08.837 回答
4

试试这个 :

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

如上所述

php/symfony/doctrine 内存泄漏?

乔丹费尔德斯坦的回答不是我的。

于 2011-07-11T11:40:58.353 回答
4

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

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

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

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

于 2012-04-27T02:42:38.290 回答
2

抱歉,我知道这是一个迟到的答案,但可以帮助某人。

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

sfConfig::set('sf_debug', false);
于 2011-04-27T16:54:55.543 回答
1

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

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

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

于 2011-02-07T20:24:23.137 回答
1

在 Doctrine Query 上使用 fetchOne() 时要小心。此函数调用不会在 SQL 上附加“限制 1”

如果您只需要从数据库中获取一条记录,请确保:

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

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

您可以看到 fetchOne() 将首先从 DB 作为集合获取,然后返回第一个元素。

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;
}
于 2012-05-16T10:00:07.547 回答
0

还值得研究:

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

于 2011-02-07T20:10:11.913 回答
0

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

例如使用类似的东西:

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

代替:

$query = self::createQuery("q")->
  ->select('*')
  ...
于 2012-04-30T09:38:21.063 回答