如何改善功能以提高内存效率?

时间:2019-07-17 22:44:42

标签: python memory aws-lambda boto3

我正在用Python 3.6编写AWS Lambda 我有大量的大空间分隔文本文件,我需要遍历这些文件并提取前N行(在本例中为1000行)文本。一旦有了这些行,我需要将它们放入一个新文件中并将其上传到S3。

我也不是python开发人员,所以语言和环境对我来说是新的。

现在,我正在收集S3对象摘要,并针对它们中的每一个进行检查,然后获取对象的数据,将其作为类似于文件的对象打开,并将输出变量打开为一个类似文件的对象,然后进行处理。

我已经给我的Lambda 3GB RAM,但是lambda在处理任何文件之前已经用完了内存(每个文件大约800MB,其中大约210个)。

int

为了完整起见,我将整个功能放在了上面,但是我认为可以改进的特定区域是处理文件处理的 for item in object_summary: # Check if the object exists, and skip it if so try: head_object_response = s3Client.head_object(Bucket=target_bucket_name, Key=item) logger.info('%s: Key alredy exists.' % item) except: # if the key does not exist, we need to swallow the 404 that comes from boto3 pass # and then do our logic to headify the files logger.info('Key does not exist in target, headifying: %s' % item) # If the file doesn't exist, get the full object s3_object = s3Client.get_object(Bucket=inputBucketName, Key=item) long_file = s3_object['Body']._raw_stream.data file_name = item logger.info('%s: Processing 1000 lines of input.' % file_name) ''' Looks like the Lambda hits a memory limit on the line below. It crashes with 2500MB of memory used, the file it's trying to open at that stage is 800MB large which puts it over the max allocation of 3GB ''' try: with open(long_file, 'r') as input_file, open(file_name, 'w') as output_file: for i in range(1000): output_file.write(input_file.readline()) except OSError as exception: if exception.errno ==36: logger.error('File name: %s' %exception.filename) logger.error(exception.__traceback__) 块。

我说对了吗?我还有其他地方可以改善吗?

2 个答案:

答案 0 :(得分:1)

想想简单些。

我建议每个lambda调用只处理一个文件,那么您应该轻松地位于3GB之内。无论如何,随着最终要处理的文件数量的增加,您的lambda函数将达到最大 15分钟执行限制,因此最好考虑将lambda处理成大致一致大小的块。

如有必要,您可以引入中间的分块器lambda函数来分块处理。

如果您的文件实际上只有800MB,我认为就内存而言,您的处理应该可以。输入文件可能仍在流式传输中,您可能想要尝试删除它(del s3_object['Body']?)

from io import StringIO

def handle_file(key_name):
    # Check if the object exists, and skip it
    try:
        head_object_response = s3Client.head_object(
            Bucket=target_bucket_name, 
            Key=item
        )
        logger.info(f'{item} - Key already exists.')   
        return None, 0
    except ClientError as e:
        logger.exception(e)
        logger.info(f'{item} - Does Not exist.')           

    # If the file doesn't exist, get the full object
    s3_object = s3Client.get_object(Bucket=inputBucketName, Key=item)
    long_file = StringIO(s3_object['Body'])

    max_lines = 1000
    lines = []
    for n, line in enumerate(long_file):
        lines.append(line)
        if len(lines) == max_lines:
            break

    output = StringIO()
    output.writelines(lines)    
    output.seek(0)
    response = s3Client.put_object(Body=output, Bucket=outputBucketName, Key=item)
    return item, len(lines)

作为一个旁注,我真的建议zappa(如果您使用lambda),这会使lambda开发变得有趣。 (这将使使用Asynchronous Task Execution在同一代码中分块代码段变得容易)

答案 1 :(得分:0)

尝试检查您的日志或回溯,以查找错误的确切行-您在代码中指向的点实际上一次将读取一行(操作系统在幕后进行了封装,但这将是最多几百KB)。

诸如s3Client.get_object(Bucket=inputBucketName, Key=item)之类的方法或诸如long_file = s3_object['Body']._raw_stream.data之类的属性访问更有可能将文件的实际内容急切地带到内存中。

您必须检查这些文档,以及如何从S3流式传输数据并将其转储到磁盘,而不是全部存储在内存中。该属性被命名为._raw_stream,并以_开头的事实表明它是私有属性,不建议直接使用它。

此外,您正在使用pass,它什么也不做,循环的其余部分将以相同的方式运行-您可能要在那里使用continue。空的except子句(不记录错误)是Python代码中可能发生的最严重的错误-如果其中存在错误,则必须记录该错误,而不仅仅是“假装没有发生”。 (甚至是Python 3中的ilegal语法)