在我的NDB数据存储区中,我有超过200万条记录。我想将按created_at
日期分组的这些记录导出到Google云端存储上的CSV文件中。我计算出每个文件大约是1GB。
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 :(得分: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()
在很多情况下,它一直在为我工作。
答案 1 :(得分:1)
我终于明白了。由于所有数据都在NDB数据存储区中,因此我无法在本地测试所有数据,因此我发现logging.info("Memory Usage: %s", runtime.memory_usage().current())
非常有用。 (使用from google.appengine.api import runtime
导入)。
问题是&#34; In-Context Cache&#34; 查询结果被写回到上下文缓存中。 More information. 请参阅example to disable the In-Context Cache了解实体种类。
虽然我的计算有点错误。生成的CVS文件大约300 MB。它会在5分钟内生成/保存到Google云端存储中。
峰值内存消耗约为480MB。
相比之下,如上面评论中@brian所建议的gc.collect()
循环(link)中添加了while True:
,内存消耗峰值约为260MB。但是花了很长时间,大约20分钟。
答案 2 :(得分:0)
上下文缓存可能是您的问题的一部分,但fetch_page通常是一个漏洞的方法。如果您正在重复查询,请将您的工作包装在@ndb.toplevel中,以便在查询之间清除队列,垃圾收集可以更有效。