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中,以便在查询和垃圾收集之间清除队列可以更有效。