我正在使用 Symfony 1.4 和 Doctrine。
到目前为止,我使用 Symfony 运行任务没有问题。但是现在我必须导入大量数据并将它们保存在数据库中,我得到了臭名昭著的
“致命错误:XXXX 字节的允许内存大小已用尽”
在此导入期间,我只创建新对象、设置一些字段并保存它们。
我很确定这与我在保存数据时创建的对象数量有关。但是,取消设置这些对象并没有做任何事情。
是否有任何最佳实践来限制 Symfony 中的内存使用?
我正在使用 Symfony 1.4 和 Doctrine。
到目前为止,我使用 Symfony 运行任务没有问题。但是现在我必须导入大量数据并将它们保存在数据库中,我得到了臭名昭著的
“致命错误:XXXX 字节的允许内存大小已用尽”
在此导入期间,我只创建新对象、设置一些字段并保存它们。
我很确定这与我在保存数据时创建的对象数量有关。但是,取消设置这些对象并没有做任何事情。
是否有任何最佳实践来限制 Symfony 中的内存使用?
我遇到过这个问题,我发现有一些技术确实有助于 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()
否则不会清除循环引用。
试试这个 :
Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
如上所述
乔丹费尔德斯坦的回答不是我的。
减少任务中使用的内存量的另一个提示是禁用查询分析器。大量查询往往会使任务使用越来越多的内存。
为此,通过添加以下行在您的 database.yml 配置文件中创建一个新的任务环境:
task:
doctrine:
class: sfDoctrineDatabase
param:
profiler: false
然后将您的任务设置为在“任务”环境中运行。如果您的查询处于循环中,它应该有助于保持内存使用稳定。
抱歉,我知道这是一个迟到的答案,但可以帮助某人。
另一个潜在的巨大内存节省是确保没有为该任务启用 Symfony 的调试模式。在几个长期运行的任务中,我添加了这一行,即使在我优化了水合模式之类的东西之后,它也减少了大约 40% 的 RAM 使用量。
sfConfig::set('sf_debug', false);
我对 symfony 的 PHP 批处理作业也有同样的问题——如果它们运行很长时间并使用大量数据,它们往往会膨胀,即使我制作了一个调用许多单独 PHP 进程的包装器,它也没有没有帮助。
正因为如此,我用 Perl 的 DBI 重写了我更大的批处理作业,它们可靠且易于管理。
我并不是说这是最好的答案,只是同情并提供我的经验。可能有一种方法可以让 PHP 表现得更好。
在 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;
}
还值得研究:
gc_collect_cycles — 强制收集任何现有的垃圾循环
还要尝试将查询中的(选择)字段限制为您真正需要的字段。
例如使用类似的东西:
$query = self::createQuery("q")->
->select('id','title','price')
...
代替:
$query = self::createQuery("q")->
->select('*')
...