将大文件写入Blob存储时内存不足
我正在使用一个实验性的 blobstore 文件 API 来写一个包含一些事件数据的 CSV 文件。因为数据量很大,所以我在分批写入。我的代码在后台运行,有很多时间,但我快没内存了,我不明白为什么。
这是我的代码:
from __future__ import with_statement
from google.appengine.api import files
q = Event.all()
events = q.fetch(50)
while events:
with files.open(blobname, 'a') as f:
buf = StringIO()
for event in events:
buf.write(event.id)
buf.write(',')
buf.write(`event.logged`)
buf.write(',')
buf.write(event.type)
buf.write(',')
buf.write(event.timestamp)
buf.write(',')
needAmpersand = False
for prop in event.dynamic_properties():
if needAmpersand:
buf.write('&')
needAmpersand = True
buf.write(prop + '=' + str(getattr(event, prop)))
buf.write('\n')
f.write(buf.getvalue())
buf.close()
events = q.fetch(50)
files.finalize(blobname)
这段代码在处理事件的循环中大约运行了 20 次,然后就因为用掉了超过 140 MB 的内存而中止。Event 是这个应用程序特有的数据库模型。简单来说,Event 就是记录远程机器上发生的事情,之后这些事件会通过一个叫做 map reduce 的操作来生成统计数据,现在我只想下载它们。我们的数据库里有成千上万的事件(以后我们会换一种存储方式,但现在就这样)。
我注意到,使用 f.open
会在每次完成 with 语句后调用 f.close
,因为 f.close()
是通过 f.__exit__()
来调用的。
之前的代码版本只是对每个要写入 'StringIO' 的元素调用了 f.write(..)。那个版本的内存消耗得更快,但其他表现类似。这个代码中仍然有一些东西导致了内存泄漏。
谁能帮帮我?
更新
我刚刚尝试注释掉 f.write(buf.getvalue()),虽然这显然不会创建一个包含任何内容的 blobstore 项目,但它最终还是完成了,处理了所有的 Event 实体。我是不是漏掉了什么,还是说 f.write() 会导致内存泄漏,或者会把所有内容缓冲到 finalize() 之前?
1 个回答
当你调用 f.write(buf.getvalue())
时,其实是在让 StringIO
把自己变成一个完整的内存对象,然后把这个对象传给 f.write
。这样做会比较耗费资源。
你可以试试 buf.seek(0)
,这个命令会把指针移回到流的开头,然后直接用 f.write(buf)
。因为 StringIO
是一种类似文件的对象,f.write
应该可以像读取流一样读取它。
可以看看这个 源代码,文档或代码里并没有明确说明 file_service_pb.AppendRequest
是否能处理 StringIO
。你可以试试看。