2016-04-25 87 views
1

我有幾個數據庫管理任務需要經過數據庫中的每條記錄。這是我的理解是與CakePHP的ORM 3.x的,我可以做這樣的事情,它會永遠只能在內存中有一條記錄時間:每次讀取記錄時內存使用量都會增加

$records = TableRegistry::get('Whatever')->find(); 
foreach ($records as $record) { 
    // do some processing 
} 

然而,這最終有崩潰「內存不足「的例外。我已經添加了一些memory_get_peak_usage的日誌記錄,並且每次迭代都會增加,即使除了foreach循環內發生的日誌記錄之外沒有其他任何內容。循環中每次增加約12K。

我正在運行3.2.7,並且結果類似,無論我是否啓用了調試和/或SQL日誌記錄。將頻繁撥打的電話添加到gc_collect_cycles()只會降低過程速度,這對內存使用量沒有幫助。

這是預計,或者一個錯誤?如果前者,有什麼我可以以不同的方式在這個代碼,以防止它? (很顯然,我可以小批量處理它,但這不是一個很好的解決方案。)

+0

您是否嘗試過關閉[**結果緩衝**](http://book.cakephp.org/3.0/en/orm/retrieving-data -and-resultsets.html#工薪與對結果集)? – ndm

+0

@ndm,聽起來很有希望,所以我跑了幾個快速測試,但有些奇怪的是似乎顯示內存使用增加*更快*緩衝關閉。完全可能的是,我在測試中做了錯誤的測試......其他測試崩潰了,告訴我「在其他未緩衝的查詢處於活動狀態時無法執行查詢」,這對我來說可能是一種破壞行爲,在這種情況下。我將不得不考慮這個問題,看看是否有解決方案,對代碼的干擾性小於運行我需要小批量處理的大查詢。 –

回答

0

從我的理解,它是預期的行爲,因爲當您開始迭代對象時執行帶有ORM的查詢生成($記錄)。因此所有的數據都被加載到內存中,然後你逐個遍歷每個條目。

如果你想限制內存使用量,我建議你看看limitoffset。有了這些,您可以提取要使用的子集,從而限制內存使用量。

+0

我以爲新的ORM只能以這種方式一次檢索一個記錄。如果我在結果集上調用'toArray',那麼肯定它會一次加載所有內容,但事實是它隨着它的增加而增加似乎表明它沒有。 –

+0

我覺得你很困惑[懶惰的評價](http://book.cakephp.org/3。0/EN/ORM /查詢builder.html#如何,都查詢 - 惰性計算)。我不是ORM的專家,但我非常確定要學習[查詢構建器](http://book.cakephp.org/3.0/en/orm/query-builder.html)和[collections](http ://book.cakephp.org/3.0/en/core-libraries/collections.html)將幫助你優化你的查詢:)另外,我剛剛看了[Advanved querying](https://www.youtube.com/watch? v = rBRy5BiCeew)這種類型的演示ORM的力量。可能對你也有用:) –

+0

如果它一次將所有記錄檢索到內存中,然後一次一個地檢查它們,它會立即崩潰。 (或者,如果我讀了一個較小的數字,內存將立即跳轉到其高峯使用狀態,然後只是坐在那裏相對不變)。每次迭代內存使用量穩定增加直到崩潰告訴我這是事實一次只能提取一條記錄,但在進入下一條記錄時不會處理該對象。這對我來說很清楚;也許我沒有解釋得很好。 –

0

CakePHP 3.x ORM內置了用於ResultSet對象的查詢緩存。當您對結果集進行迭代時,實體將存儲在內部數組中。這樣做是爲了讓你可以倒回迭代器並重新循環。

如果您打算只迭代一次較大的結果集,並且想要減少內存使用量,則必須禁用結果緩衝。

$records = TableRegistry::get('Whatever')->find()->bufferResults(false); 
foreach ($records as $record) { 
    // do some processing 
} 

緩衝關閉時,實體從結果集中取出,之後應該沒有對它的引用。

此功能的文檔不在CakePHP書中,但它應該是。

這裏的API參考:

https://api.cakephp.org/3.3/class-Cake.Database.Query.html#_bufferResults