我正在使用Symfony 1.4和Doctrine。
到目前为止,我使用Symfony运行任务没有问题。 但是现在我必须导入相当多的数据并将它们保存在数据库中,我得到了臭名昭着的
“致命错误:允许的内存大小 XXXX字节耗尽“
在导入过程中,我只创建新对象,设置几个字段并保存它们。
我很确定这与我在保存数据时创建的对象数量有关。取消设置这些对象不会做任何事情。
在Symfony中是否有限制内存使用的最佳做法?
答案 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('*')
...