2012-03-14 82 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