Python中的S3备份内存使用情况

1 投票
2 回答
545 浏览
提问于 2025-04-15 20:32

我现在使用WebFaction来做网站托管,选择的是基础套餐,提供80MB的内存。这个内存对我们目前的需求来说已经足够了,除了备份的部分。我们每天会把备份上传到S3。

备份的过程是这样的:先把数据库的数据导出,然后把所有文件打包成一个压缩文件,文件名里包含备份的日期,最后使用亚马逊提供的Python库把这个文件上传到S3。

不过,似乎(虽然我不太确定)是我的代码在读取文件时,或者是S3的代码在处理时,把整个文件都加载到了内存里。今天的备份文件大约有320MB,这样就占用了大约320MB的内存。这个情况导致WebFaction停止了我们所有的进程,结果备份没能完成,我们的网站也因此宕机了。

所以我想问:有没有办法不把整个文件都加载到内存里,或者有没有其他更好用的Python S3库,能更有效地使用内存?理想情况下,内存使用量最好控制在60MB以内!如果这做不到,我该如何把文件分割成几个部分,然后分别上传呢?

谢谢你的帮助。

这是我备份脚本中导致进程停止的代码部分:

filedata = open(filename, 'rb').read()
content_type = mimetypes.guess_type(filename)[0]
if not content_type:
    content_type = 'text/plain'
print 'Uploading to S3...'
response = connection.put(BUCKET_NAME, 'daily/%s' % filename, S3.S3Object(filedata), {'x-amz-acl': 'public-read', 'Content-Type': content_type})

2 个回答

2

虽然有点晚了,但我也遇到过同样的问题,所以我来分享一下我的解决办法。

简单来说:在Python 2.6及以上版本中,可以!这是因为从2.6版本开始,httplib支持类似文件的对象。所以你只需要...

fileobj = open(filename, 'rb')
content_type = mimetypes.guess_type(filename)[0]
if not content_type:
    content_type = 'text/plain'
print 'Uploading to S3...'
response = connection.put(BUCKET_NAME, 'daily/%s' % filename, S3.S3Object(fileobj), {'x-amz-acl': 'public-read', 'Content-Type': content_type})

详细说说...

S3.py这个库使用Python的httplib来进行connection.put()的HTTP请求。你可以在源代码中看到,它只是把data参数传给了httplib的连接。

中...

    def _make_request(self, method, bucket='', key='', query_args={}, headers={}, data='', metadata={}):

        ...

        if (is_secure):
            connection = httplib.HTTPSConnection(host)
        else:
            connection = httplib.HTTPConnection(host)

        final_headers = merge_meta(headers, metadata);
        # add auth header
        self._add_aws_auth_header(final_headers, method, bucket, key, query_args)

        connection.request(method, path, data, final_headers) # <-- IMPORTANT PART
        resp = connection.getresponse()
        if resp.status < 300 or resp.status >= 400:
            return resp
        # handle redirect
        location = resp.getheader('location')
        if not location:
            return resp
        ...

如果我们看看Python httplib的文档,我们可以看到...

HTTPConnection.request(method, url[, body[, headers]])

这个方法会使用指定的HTTP请求方法和URL向服务器发送请求。如果有body参数,它应该是一个在头部结束后要发送的数据字符串。或者,它也可以是一个打开的文件对象,这样就会发送文件的内容;这个文件对象需要支持fileno()read()方法。头部的Content-Length会自动设置为正确的值。headers参数应该是要与请求一起发送的额外HTTP头部的映射。

在2.6版本中更改:body可以是一个文件对象。

0

不要把整个文件都读到你的filedata变量里。你可以使用一个循环,每次读取大约60MB的数据,然后再提交给亚马逊。

backup = open(filename, 'rb')
while True:
    part_of_file = backup.read(60000000) # not exactly 60 MB....
    response = connection.put() # submit part_of_file here to amazon

撰写回答