简体   繁体   English

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

[英]How to use less memory while running a task in Symfony 1.4?

I'm using Symfony 1.4 and Doctrine. 我正在使用Symfony 1.4和Doctrine。

So far I had no problem running tasks with Symfony. 到目前为止,我使用Symfony运行任务没有问题。 But now that I have to import a pretty big amount of data and save them in the database, I get the infamous 但是现在我必须导入大量数据并将其保存在数据库中,因此声名狼藉

"Fatal Error: Allowed memory size of XXXX bytes exhausted" “严重错误:允许的XXXX字节内存容量用尽”

During this import I'm only creating new objects, setting a few fields and saving them. 在此导入期间,我仅创建新对象,设置一些字段并保存它们。

I'm pretty sure it has something to do with the number of objects I'm creating when saving data. 我很确定这与保存数据时创建的对象数量有关。 Unsetting those objects doesn't do anything though. 取消设置这些对象不会执行任何操作。

Are there any best practices to limit memory usage in Symfony? 是否有任何最佳实践来限制Symfony中的内存使用?

I've come across this, and there's a couple of techniques I found really helped with Doctrine's extensive memory usage. 我遇到过这种情况,并且发现有两种技术确实有助于Doctrine广泛使用内存。

1: Where possible, hydrate your Doctrine query results down to an array. 1:在可能的情况下,将您的Doctrine查询结果合并为一个数组。 You can do this as follows eg: 您可以按以下方式执行此操作,例如:

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

This forces Doctrine to NOT create large objects, but instead reduces it to an array. 这迫使Doctrine不创建大型对象,而是将其简化为数组。 Obviously bear in mind that if you do this, you lose the ability to call methods etc, so this is only good if you're using it for reading field values etc. 显然要记住,如果执行此操作,则会失去调用方法等的能力,因此,只有在将其用于读取字段值等时,这才是好的。

2: Free your results after execution. 2:执行后释放结果。 This is documented in a tiny area of the Doctrine docs, but it's really helped out the import task I was using: 在Doctrine文档的一小部分中对此进行了记录,但这确实帮助了我正在使用的导入任务:

$query->free();

That's it. 而已。 You can also do this on objects that you've created, eg $myObj->free(); 您也可以在创建的对象上执行此操作,例如$myObj->free(); and this forces Doctrine to remove all the circular references that it creates. 并迫使Doctrine删除它创建的所有循环引用。 Note that circular references are automatically freed from PHP 5.3 onwards on deletion of an object via the PHP scope or unset() , but before that you'll need to do it yourself. 请注意,在通过PHP作用域或unset()删除对象后,会自动从PHP 5.3起释放循环引用,但在此之前,您需要自己进行操作。

Unsetting variables after you've used them also helps, although do this in conjunction with the free() method above as mentioned, as unset() won't clear the circular refs otherwise. 使用变量后取消设置变量也有帮助,尽管如上所述将其与上述free()方法结合使用,否则unset()不会清除循环引用。

Another hint for reducing the amount of memory used inside a task is to disable the query profiler. 减少任务内部使用的内存量的另一个提示是禁用查询分析器。 A big number of queries tends to make the task using more and more memory. 大量查询趋向于使任务使用越来越多的内存。

To do so create a new task environment in your database.yml config file by adding the following lines: 为此,通过添加以下几行在您的database.yml配置文件中创建一个新的任务环境:

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

Then setup your task to run in "task" environment. 然后将您的任务设置为在“任务”环境中运行。 It should help to keep memory usage stable if your queries are in a loop. 如果您的查询处于循环中,则应有助于保持内存使用率稳定。

Try this : 尝试这个 :

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

as mentioned on 如前所述

php/symfony/doctrine memory leak? PHP / Symfony /主义内存泄漏?

Answer from Jordan Feldstein not mine. 乔丹·费尔德斯坦的回答不是我的。

Sorry I know this is a late answer, but could help someone. 抱歉,我知道这是一个较晚的答案,但可以帮助某人。

Another potentially huge memory saver is to make sure Symfony's debug mode is not enabled for that task. 另一个潜在的巨大内存保护程序是确保未为该任务启用Symfony的调试模式。 In a couple of long-running tasks I added this line and it cut down the RAM usage about 40% even after I had optimized things like hydration mode. 在一些长期运行的任务中,我添加了这一行,即使在优化水合模式等功能后,它也将RAM使用率降低了约40%。

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

Caution with fetchOne() on Doctrine Query. 使用Doctrine查询时的fetchOne()警告。 This function call will not append "Limit 1" on SQL 此函数调用不会在SQL上附加“限制1”

If you just need to get one records from DB, make sure: 如果只需要从数据库获取一条记录,请确保:

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

The memory usage is tremendous dropped on large table. 大表上的内存使用量大大下降。

You could see fetchOne() will fetch from DB as a collection first then return the first element. 您可能会看到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;
}

I've had the same problem with PHP batch jobs for symfony--if they run for a long time and use a lot of data they tend to balloon, and even if I made one wrapper that invoked many separate PHP processes, it didn't help. 我对于symfony的PHP批处理作业也遇到了同样的问题-如果它们运行很长时间并且使用大量数据,它们往往会膨胀,即使我创建了一个包装程序来调用许多单独的PHP进程,也没有做到这一点。没有帮助。

Because of this, I've rewritten my bigger batch jobs with Perl's DBI and they're reliable and manageable. 因此,我用Perl的DBI重写了较大的批处理作业,它们是可靠且可管理的。

I'm not suggesting this is the best answer, just sympathizing and offering my experience. 我并不是说这是最好的答案,只是同情并提供我的经验。 There's probably a way to make PHP behave better. 可能有一种方法可以使PHP表现得更好。

Also try to limit the (select) fields in your query only to those you really need. 还尝试将查询中的(选择)字段仅限制为您真正需要的字段。

for example use something like: 例如使用类似:

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

instead of: 代替:

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

Also worth looking into: 还值得研究:

gc_collect_cycles — Forces collection of any existing garbage cycles gc_collect_cycles —强制收集任何现有的垃圾循环

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM