我正在使用Symfony 1.4和Doctrine。
到目前为止,我在Symfony中运行任务没有任何问题。 但是现在我需要导入大量数据并将它们保存在数据库中,我遇到了臭名昭著的
"Fatal Error: Allowed memory size of XXXX bytes exhausted"
在此导入期间,我只创建新对象,设置一些字段并保存它们。
我非常确定这与在保存数据时创建的对象数量有关。然而,取消设置这些对象没有任何效果。
在Symfony中有什么最佳实践来限制内存使用?
我正在使用Symfony 1.4和Doctrine。
到目前为止,我在Symfony中运行任务没有任何问题。 但是现在我需要导入大量数据并将它们保存在数据库中,我遇到了臭名昭著的
"Fatal Error: Allowed memory size of XXXX bytes exhausted"
在此导入期间,我只创建新对象,设置一些字段并保存它们。
我非常确定这与在保存数据时创建的对象数量有关。然而,取消设置这些对象没有任何效果。
在Symfony中有什么最佳实践来限制内存使用?
我遇到了这个问题,我发现有一些技巧可以有效地减少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()
不会清除循环引用。
试一下:
Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
Jordan Feldstein 的回答,不是我的。
为了减少任务内存使用量,另一个提示是禁用查询分析器。 大量的查询往往会使任务使用越来越多的内存。
要做到这一点,请在您的database.yml配置文件中创建一个新的任务环境,并添加以下行:
task:
doctrine:
class: sfDoctrineDatabase
param:
profiler: false
然后在“task”环境中设置您的任务运行。如果您的查询在循环中,它应该有助于保持内存使用稳定。
sfConfig::set('sf_debug', false);
在使用Doctrine查询中的fetchOne()函数时要小心。这个函数调用不会在SQL语句中添加"Limit 1"。
如果你只需要从数据库获取一条记录,请确保:
$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;
}
我在symfony的PHP批处理作业中遇到了同样的问题——如果它们运行很长时间并使用大量数据,它们往往会膨胀,即使我制作了一个调用许多单独的PHP进程的包装器,也没有帮助。
因此,我用Perl的DBI重写了我的更大的批处理作业,它们是可靠和可管理的。
我不是在建议这是最好的答案,只是表示同情并提供我的经验。可能有一种方法可以使PHP表现更好。
同时尝试仅在查询中限制需要的(选择)字段。
例如使用以下内容:
$query = self::createQuery("q")->
->select('id','title','price')
...
改为:
$query = self::createQuery("q")->
->select('*')
...