2012-03-14 90 views
43

实际上,我对symfony2和doctrine2的组合有很多麻烦。我必须处理大量的数据集(大约2-3百万次的写入和读取),并且必须做很多额外的工作以避免内存不足。内存泄漏Symfony2 Doctrine2 /超出内存限制

我figgured了2个主要的点,即“泄漏”荷兰国际集团的内存(他们实际上是不是真的泄漏,但是分配了很多)

  1. EntityManager的实体存储(我不知道这样做的真实姓名一个),好像它一直都处理的entites,你必须清除此存储regulary与

    $entityManager->clear()
  2. 教义实现QueryCache - 它缓存所有使用的查询,我发现的唯一配置是你能决定什么你想要我们的那种Cache即我没有找到一个全局禁用既不是一个有用的标志为每个查询来禁用它。 所以通常与功能禁用它的每个查询的对象

     
    $qb = $repository->createQueryBuilder($a); 
    $query = $qb->getQuery(); 
    $query->useQueryCache(false); 
    $query->execute(); 
    

所以..这就是所有我现在想通了.. 我的问题是:

有没有一种简单的方法来否认来自Entitymanagerstorage的一些对象? 有没有办法在entitymanager中设置querycache的使用? 我可以在symonfony教义配置中配置这种缓存行为吗?

将是非常酷的,如果有一个人对我一些很好的建议..否则可能帮助一些新人..

CYA晚

+2

的D2 ORM层是不是真的设计用于大规模批量处理。使用DBAL层和使用数组可能会更好。 – Cerad 2012-03-14 20:22:05

+1

与运行** - 无调试**有很大帮助(在调试模式下探查保留有关的每一个查询的信息在内存中) – arnaud576875 2012-09-14 11:29:56

回答

83

一点点,但是我觉得我刚刚发现了一个thread on Google Groups本杰明Eberlei,回答你的问题:因为默认情况下SQL连接的记录规定由Doctrine Configuration Reference设置为kernel.debug的价值,因此,如果您有实例AppKernel与调试设置为的SQL命令获得存储在内存中的每个迭代配给。

您应该实例AppKernel到,设置登录你假配置阳明,或任一使用EntityManager

$em->getConnection()->getConfiguration()->setSQLLogger(null); 
+4

好样的!我们几个月来一直在解决这个问题! – jsalvata 2013-01-30 15:48:14

+3

这是SF2的事情。你真的**需要阅读文档和代码以了解它是如何工作的。有一天,我们发现我们没有在请求之间缓存DQL和元数据。我们这样做了[结束](https://coderwall.com/p/ry1y0a),请求的速度比更改前快两倍 – Sergi 2013-02-02 16:11:19

+4

这非常有帮助。我有一个我写的控制台命令(就像是一个“守护进程”类型的命令),一直运行内存不足,并且在对象管理器上使用'clear()'方法是不够的。禁用这个SQL记录器的窍门。然而,由于我在一个控制台命令中,我不得不使用'$ this-> getContainer() - > get('doctrine') - > getEntityManager()'来实际到达实体管理器来执行此操作。 – jzimmerman2011 2013-03-17 16:45:43

17

尝试运行命令之前手动设置SQLLogger为null与- 无调试。在调试模式下,分析器保留有关内存中每个查询的信息。

+0

感谢这个结合关闭sqllogging真的帮助 – Chausser 2014-02-20 01:51:13

+0

这解决了模板引擎和树枝的问题。在简单模板上运行循环似乎是开发中的一个memory_leak。 – fyrye 2014-07-09 18:00:51

+0

我一直在修补几个小时,当我发现这个......它解决了这个问题。谢谢:) – indriq 2014-11-20 20:51:04

3

接到学说开发商本身symfony的住在柏林的一些“有趣”的消息 - 他们说,这对大批量,我们不应该使用ORM ..它只是没有有效的空中接力

建立这样的东西

..是啊..也许他们是对的xD

+0

我们可能不得不沿着这条路走......即使他们知道这件事,它也会吸引人,但这并没有得到解决 – 2013-03-04 02:11:20

0

尝试禁用存在的任何Doctrine缓存。 (如果您不使用APC /其他缓存,则使用内存)。

删除查询缓存

$qb = $repository->createQueryBuilder($a); 
$query = $qb->getQuery(); 
$query->useQueryCache(false); 
$query->useResultCache(false); 
$query->execute(); 

有没有办法来全局禁用它

而且这是清除可能的帮助(从here

$connection = $em->getCurrentConnection(); 
$tables = $connection->getTables(); 
foreach ($tables as $table) { 
    $table->clear(); 
} 
3

替代根据标准Doctrine2文档,您需要手动清除或增加实体。

除此之外,启用分析时(如在默认的开发环境中)。 Symfony2中的DoctrineBundle配置了几个记录器使用相当多的内存。您可以完全禁用日志记录,但它不是必需的。

一个有趣的副作用是记录仪同时影响Doctrine ORM和DBAL。记录器之一将导致使用默认记录器服务的任何服务的额外内存使用量。禁用所有这些将是理想的命令 - 因为分析器尚未使用。

这里是你可以做什么来禁用内存密集型记录器,同时保持谱在Symfony2中的其他部分启用:

$c = $this->getContainer(); 
/* 
* The default dbalLogger is configured to keep "stopwatch" events for every query executed 
* the only way to disable this, as of Symfony 2.3, Doctrine Bundle 1.2, is to reinistiate the class 
*/ 

$dbalLoggerClass = $c->getParameter('doctrine.dbal.logger.class'); 
$dbalLogger = new $dbalLoggerClass($c->get('logger')); 
$c->set('doctrine.dbal.logger', $dbalLogger); 

// sometimes you need to configure doctrine to use the newly logger manually, like this 
$doctrineConfiguration = $c->get('doctrine')->getManager()->getConnection()->getConfiguration(); 
$doctrineConfiguration->setSQLLogger($dbalLogger); 

/* 
* If profiling is enabled, this service will store every query in an array 
* fortunately, this is configurable with a property "enabled" 
*/ 
if($c->has('doctrine.dbal.logger.profiling.default')) 
{ 
    $c->get('doctrine.dbal.logger.profiling.default')->enabled = false; 
} 

/* 
* When profiling is enabled, the Monolog bundle configures a DebugHandler that 
* will store every log messgae in memory. 
* 
* As of Monolog 1.6, to remove/disable this logger: we have to pop all the handlers 
* and then push them back on (in the correct order) 
*/ 
$handlers = array(); 
try 
{ 
    while($handler = $logger->popHandler()) 
    { 
     if($handler instanceOf \Symfony\Bridge\Monolog\Handler\DebugHandler) 
     { 
      continue; 
     } 
     array_unshift($handlers, $handler); 
    } 
} 
catch(\LogicException $e) 
{ 
    /* 
    * As of Monolog 1.6, there is no way to know if there's a handler 
    * available to pop off except for the \LogicException that's thrown. 
    */ 
    if($e->getMessage() != 'You tried to pop from an empty handler stack.') 
    { 
     /* 
     * this probably doesn't matter, and will probably break in the future 
     * this is here for the sake of people not knowing what they're doing 
     * so than an unknown exception is not silently discarded. 
     */ 

     // remove at your own risk 
     throw $e; 
    } 
} 

// push the handlers back on 
foreach($handlers as $handler) 
{ 
    $logger->pushHandler($handler); 
} 
9
  1. 设置SQL记录为空

$em->getConnection()->getConfiguration()->setSQLLogger(null);

  1. 手动调用函数gc_collect_cycles()$em->clear()

$em->clear(); gc_collect_cycles();

不要忘记设置zend.enable_gc为1,或手动使用之前调用gc_enable()gc_collect_cycles()

  • 添加--no-debug选项,如果你从运行指令安慰。
  • 9

    1.关闭记录和分析在app/config/config.yml

    doctrine: 
        dbal: 
         driver: ... 
         ... 
         logging: false 
         profiling: false 
    

    或代码

    $this->em->getConnection()->getConfiguration()->setSQLLogger(null); 
    

    2.强制垃圾收集器。如果您主动使用CPU,则垃圾收集器会等待,并且您很快就会发现自己没有内存。

    首先启用手动垃圾收集管理。在代码的任何地方运行gc_enable()。然后运行gc_collect_cycles()强制垃圾收集器。

    public function execute(InputInterface $input, OutputInterface $output) 
    { 
        gc_enable(); 
    
        // I'm initing $this->em in __construct using DependencyInjection 
        $customers = $this->em->getRepository('AppBundle:Customer')->findAll(); 
    
        $counter = 0; 
        foreach ($customers as $customer) { 
         // process customer - some logic here, $this->em->persist and so on 
    
         if (++$counter % 100 == 0) { 
          $this->em->flush(); // save unsaved changes 
          $this->em->clear(); // clear doctrine managed entities 
          gc_collect_cycles(); // PHP garbage collect 
    
          // Note that $this->em->clear() detaches all managed entities, 
          // may be you need some; reinit them here 
         } 
        } 
    
        // don't forget to flush in the end 
        $this->em->flush(); 
        $this->em->clear(); 
        gc_collect_cycles(); 
    } 
    

    如果你的表是非常大的,不使用findAll。使用迭代器 - http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html#iterating-results

    0

    我刚刚发布了一些关于使用Symfony控制台命令和Doctrine进行批处理的技巧here