2015-10-30 20 views
4

我試圖在使用Doctrine2和Symfony2夾具束的MySQL數據庫中插入大量數據(30 000+行)。我看着the right way to do it。我看到很多關於內存泄漏和主義的問題,但對我來說沒有令人滿意的答案。它經常出現Doctrine clear()函數。內存使用情況隨着教條批量插入

所以,我做的這個各種形狀:

while (($data = getData()) { 
    $iteration++; 

    $obj = new EntityObject(); 
    $obj->setName('henry'); 
    // Fill object... 

    $manager->persist($obj); 

    if ($iteration % 500 == 0) { 
     $manager->flush(); 
     $manager->clear(); 

     // Also tried some sort of: 
     // $manager->clear($obj); 
     // $manager->detach($obj); 
     // gc_collect_cycles(); 
    } 
} 

PHP內存還是去野外,在flush()之後(我敢肯定的說)。實際上,每次刷新實體時,內存都會根據批量大小和實體的不同而有所增加,直到達到致命的允許的內存大小用盡錯誤。有一個非常小的實體,它可以工作,但內存消耗增加太多:幾MB,而它應該是KB。

clear(),detach()或調用GC似乎沒有任何效果。它只會清除一些KB。

我的方法有缺陷嗎?我錯過了什麼地方?這是一個錯誤嗎?

更多信息

  • 沒有flush()記憶幾乎沒有移動;
  • 降低批次不會改變結果;
  • 數據來自需要進行消毒的CSV;

EDIT(局部的溶液)

@qooplmao帶來了解決方案,顯著減少內存消耗,禁用學說SQL記錄器:$manager->getConnection()->getConfiguration()->setSQLLogger(null);

然而,它仍然是異常高的和不斷增加。

+0

您是否嘗試降低批量(500)?它會更慢,但內存密集程度更低 –

+0

是的。我試圖降低它(100,20,1)並增加它(1000,2000,5000),沒有變化。 –

+0

這裏只是好奇,getData()返回什麼?它在哪裏獲得信息?多少?有趣的是,選擇循環與文檔相比,我挖了它 –

回答

7

我使用this resource解決了我的問題,正如@Axalix建議的那樣。

這是我修改了代碼:

// IMPORTANT - Disable the Doctrine SQL Logger 
$manager->getConnection()->getConfiguration()->setSQLLogger(null); 

// SUGGESION - make getData as a generator (using yield) to to save more memory. 
while ($data = getData()) { 
    $iteration++; 

    $obj = new EntityObject(); 
    $obj->setName('henry'); 
    // Fill object... 

    $manager->persist($obj); 

    // IMPORTANT - Temporary store entities (of course, must be defined first outside of the loop) 
    $tempObjets[] = $obj; 

    if ($iteration % 500 == 0) { 
    $manager->flush(); 

    // IMPORTANT - clean entities 
    foreach($tempObjets as $tempObject) { 
     $manager->detach($tempObject); 
    } 

    $tempObjets = null; 
    gc_enable(); 
    gc_collect_cycles(); 
    } 
} 

// Do not forget the last flush 
$manager->flush(); 

而且,最後但並非最不重要的,因爲我用這個腳本Symfony的數據燈具,在命令中加入--no-debug參數也是非常重要的。那麼內存消耗是穩定的。

+1

對$ tempObjects賦值的評論。保存批處理時,將從entityManager中分離出對象,但不清除$ tempObjects。這意味着如果您在保存2000個項目的位置分開500個對象,然後分離1000個對象,然後分離1500個對象。批次保存時清除$ tempObjects是否合理? –

+0

@JeremyQuinton你是對的,他需要在分離它們之後重置$ tempObjects。 – kunicmarko20

+0

如果我們想要插入50000個對象,但執行完這段代碼後只插入了48000個,我們如何識別未插入的對象以及如何獲得插入對象的確切數量? –

-2

我的建議是放棄批量插入的Doctrine方法。我真的很喜歡Doctrine,但我只是討厭這種散裝插入的東西。

MySQL有一個偉大的東西叫做LOAD DATA。我寧願使用它,或者即使我必須首先清理我的csv,然後執行LOAD。

如果您需要更改值,我會將csv讀取到數組$csvData = array_map("str_getcsv", file($csv));。在陣列上更改所需的任何內容並將其保存到行中。之後,使用新的.csv來加載MySQL。

爲了支持我爲什麼我不會在頂部描述的here中使用Doctrine的說法。

+0

嗯......這太遺憾了,因爲據我所知,有些人表示這不是批量INSERT的最佳工具,主要是爲了表現觀點。然而,就我而言,我並不在乎性能(它有幾千行,而不是幾百萬!)。它應該正常工作。這就是說,感謝您的幫助,我會嘗試。但是,如何方便地與LOAD DATA建立關係?我是否必須爲每個表格創建一個具有關係的單個CSV? –

+0

是的,我知道性能可能對你來說不是很重要,但是如果你能以最佳性能來做到這一點,那麼認爲它會很酷。你不能把它做到多個表,但是你可以創建一個temptable,LOAD INTO temptable,然後INSERT INTO表SELECT FROM temptable。這聽起來很棘手,但實際上這樣做更好。 –

+0

您可以使用doctrine dbal連接對象(甚至是pdo連接對象)來準備和執行sql insert語句,而不必使用mysql loadddata。這將允許您插入關係。我使用這種方法來處理5K左右相當複雜的實體。 30K可能仍然是一個延伸。 PHP本身並不是真正爲大批量作業設計的。 – Cerad