2012-10-18 82 views
2

我正在使用Doctrine和MongoDB作爲應用程序,並且有一項任務應該將數據從CSV文件導入到集合中。大約有5個不同的CSV文件,每個文件至少有450.000個條目,每年至少應該重複1到3次。使用Doctrine將大量數據插入到MongoDB集合中

目前我遍歷文件的所有行,創建一個對象,調用persist()並刷新每2.500項。

每個項目都不是很大,它有一個ID,一個10-20個字符的字符串,一個5-10個字符的字符串和一個布爾值。

我的第一個問題是: 當我沖洗每5.000項插入會明顯變慢。在我的測試環境中,沖洗5.000個物品需要約102秒,沖洗2.500個物品需要約10秒。

一段時間後,沖洗變慢。如前所述,開始時沖洗2.500件物品花了大約10秒鐘,在100.000件後,沖刷2.500件花費了近一分鐘。

我能做些什麼來優化速度?

+0

你是如何閱讀CSV文件?並且在flush()操作之後清除EntityManager?如果不是這樣,那意味着每個對象都存儲在內存中並使事情變得更慢。 –

+0

@BorisGuéry謝謝,這對我有很大的幫助。如果你添加這個答案,我會接受它。 –

回答

2

我認爲有兩個部分可以在腳本中進行優化。

  • 你讀的CSV文件,這取決於你如何加載它的方式,你要麼完全加載到內存中(例如使用file_get_contents()file()),或閱讀它通過chunck與fopen()fread() chunck。

最後一個選項是首選,因爲它只會在處理一堆行時佔用必要的內存量。

  • 您需要clear()您已經處理的對象,否則它將一直保留在內存中直到腳本結束。這意味着如果一個DC2對象使用1M內存,並且您有100,000個對象,則在腳本結尾處將使用100,000Mo。 因此,通過2,500的範圍來批量插入是一個不錯的主意,但您顯然需要從EntityManager中移除已處理的對象。

它可以通過$entityManager->clear();

clear()將清除整個EntityManager來完成,要清除一個單一的實體,您可以使用$entityManager->detach($object)


如果要分析你的內存使用情況,你可能也有興趣在功能

memory_get_usage()

memory_get_peak_usage()

+0

讀取文件沒有問題,需要大約600MB的內存才能讀取所有行並將其轉換爲「Document」對象。當我使用'清除'時,導入所有對象需要大約一個小時,這對我的用例來說很好(這項任務每年只能執行一次或兩次) –

+0

十年前,你會因爲使用600Mb的內存來讀取文件;) –

0

這個導入腳本需要使用Doctrine ODM嗎?對於整個應用程序 - 當然。但是,處理大量數據的開銷並不大,因爲PHP在保存和處理大集合方面效率不高。

您可以做的最佳優化是跳過該圖層並直接使用mongodb類,並確保您逐行讀取文件,而不是一次讀取所有文件。如果你有一些MongoDB的網絡延遲(但你不需要將它推送到5k文檔),使用batchInsert()會提高你的速度。

+0

這只是使用Doctrine的方便。當我使用@BorisGuéry提出的clear()時,它工作得很好。但是,如果我無法將其降低到可接受的時間,我將切換到直接調用MongoDB。 –

+0

那麼,在正常的應用情況下處理不超過十幾個文件是方便的。然而,它(和它們的ORM一樣)具有巨大的開銷和更多的查詢 - 更多的開銷。看來它已經讓你在批處理腳本中遇到問題的解決方案變得不那麼方便了。一個技巧是將一個Doctrine ODM插入到底部(插入一個int到一個新的集合中),然後用本地驅動程序對batchInsert執行相同的操作 - 您將看到究竟有多少Doctrine正在傷害您。 – Clarence

相關問題