【问题标题】:Export NDB Datastore records to Cloud Storage CSV file将 NDB 数据存储记录导出到 Cloud Storage CSV 文件
【发布时间】:2014-04-26 00:14:43
【问题描述】:

在我的 NDB 数据存储中,我有超过 200 万条记录。我想将这些按created_at 日期分组的记录导出到 Google Cloud Storage 上的 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 中的所有记录吗?

【问题讨论】:

标签: google-app-engine csv mapreduce google-cloud-storage app-engine-ndb


【解决方案1】:

考虑到记录的数量,很明显您确实遇到了内存错误。 请求结束时默认调用垃圾收集器,这就解释了为什么使用的内存会这样增加。

在这种情况下,我通常会在获取每个页面后使用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()

它在很多情况下都对我有用。

【讨论】:

  • 好主意。不幸的是,它给了我同样的错误。还不确定 `gcs_file.write(record)` 是如何工作的。如果此函数首先缓冲所有内容,那么这将是一个问题。
  • 不应该gc.collect()进入循环吗?
  • 他说这是伪代码,所以我猜周围还有另一个循环。他每 50 条记录 50 条,所以对我来说没有必要将 gc.collect() 放在 for 循环中。 gcs_file.write 方法应该将块写入云存储。 @mattes 你能显示你正在使用的实际代码吗?
  • 在第 26 行 (gist.github.com/mattes/9646173#file-gae_csv-py-L26) 之后添加了 gc.collect()
  • 对我来说看起来不错。你在第一次迭代时遇到内存错误吗?
【解决方案2】:

我终于明白了。由于所有数据都在 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 Cloud Storage。

内存消耗峰值约为 480MB。

相比之下,@brian 在上面的评论中建议在 while True: 循环 (link) 中添加 gc.collect(),内存消耗峰值约为 260MB。但是时间很长,大概20分钟左右。

【讨论】:

    【解决方案3】:

    上下文缓存可能是您的问题的一部分,但 fetch_page 通常是一种泄漏方法。如果您正在执行重复查询,请将您的工作包装在 @ndb.toplevel 中,以便在查询之间清除队列并且垃圾收集可以更有效。

    【讨论】:

      猜你喜欢
      • 2018-08-02
      • 2021-03-02
      • 2019-06-04
      • 2015-10-17
      • 2013-08-13
      • 1970-01-01
      • 2019-10-03
      • 2017-01-12
      • 1970-01-01
      相关资源
      最近更新 更多