3

在我的NDB數據存儲我有超過200萬分的記錄。我想將這些由created_at日期分組的記錄導出到Google雲端存儲上的CSV文件中。我計算出每個文件大約1GB。出口NDB數據存儲記錄,以雲存儲CSV文件

2014-03-18.csv, ~17000 records, ~1GB 
2014-03-17.csv, ~17000 records, ~1GB 
2014-03-18.csv, ~17000 records, ~1GB 
... 

我的第一種方法(僞代碼):

import cloudstorage as gcs 
gcs_file = gcs.open(date + '.csv', 'w') 
query = Item.query().filter(Item.created_at >= date).filter(Item.created_at < date+1day) 
records = query.fetch_page(50, cursor) 
for record in records: 
    gcs_file.write(record) 

但這個(顯然?)通向內存問題:

Error: Exceeded soft private memory limit with 622.16 MB after servicing 2 requests total 

我應該使用MapReduce的管道相反,還是有什麼辦法讓方法1工作?如果使用MapReduce:我可以過濾created_at而不迭代NDB中的所有記錄嗎?

+0

相關:http://stackoverflow.com/questions/9124398/in-google-app-engine-how-to-i-reduce-memory-consumption-as-i-write-a-file-out-t – mattes

回答

1

我終於想通了。由於所有數據都位於NDB數據存儲中,因此我無法真正地在本地測試所有內容,所以我發現logging.info("Memory Usage: %s", runtime.memory_usage().current())非常有幫助。 (用from google.appengine.api import runtime導入)。

問題是「上下文緩存」:查詢結果被寫回到上下文緩存中。 More information. 查看實體類型的example to disable the In-Context Cache

我的計算稍有不妥,但。生成的CVS文件大約爲300 MB。它會在5分鐘內生成/保存到Google雲端存儲。

Memory consumption without gc.collect()

峯值內存佔用約爲480MB。

與上面評論中的@brian所建議的while True:循環(link)中的gc.collect()相比,內存消耗高峯大約爲260MB。但花了相當長的時間,大約20分鐘。

enter image description here

4

考慮的記錄數,似乎很明顯確實,你得到一個內存錯誤。 當請求結束時,默認情況下會調用垃圾回收器,這就解釋了爲什麼使用的內存會像這樣增加。

在這種情況下我通常做的是手動gc.collect()調用垃圾收集後的每個頁面被取出。

這將是這個樣子:

import cloudstorage as gcs 
import gc 

cursor = None 
more = True 
gcs_file = gcs.open(date + '.csv', 'w') 
query = Item.query().filter(Item.created_at >= date).filter(Item.created_at < date+1day) 

while more: 
    records, cursor, more = query.fetch_page(50, cursor) 
    gc.collect() 
    for record in records: 
    gcs_file.write(record) 

gcs_file.close() 

它在很多情況下是爲我工作。

+0

好主意。不幸的是,它給了我同樣的錯誤。不知道'gcs_file.write(record)'是如何工作的。如果這個函數首先緩衝一切,那麼這將是一個問題。 – mattes

+0

不應該'gc.collect()'進入循環? – jterrace

+0

他說這是僞代碼,所以我想還有另一個循環。他每50次寫入記錄50次,所以對於我來說,不需要將gc.collect()放入for循環中。 gcs_file.write方法應該以塊的形式寫入雲存儲。 @mattes可以顯示你正在使用的實際代碼,只是爲了確保? – brian

0

的上下文中緩存可能是你的問題的一部分,但一般fetch_page是一個漏水的方法。如果您正在進行重複查詢,請將您的工作包裝在@ ndb.toplevel中,以便在查詢和垃圾收集之間清除隊列可以更有效。