首页 > 解决方案 > 如何改进我的功能以提高内存效率?

问题描述

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

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

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

我给了我的 Lambda 3GB RAM,但是 lambda 在它可以处理任何文件之前内存不足(每个文件大约 800MB,其中大约有 210 个)。

    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__)

为了完整起见,我将整个函数放在上面,但我认为我可以改进的特定区域是try: while:处理文件处理的块。

我说对了吗?还有什么地方可以改进吗?

标签: pythonmemoryaws-lambdaboto3

解决方案


想得更简单。

我建议每个 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)

作为旁注,如果您使用 lambda,我真的推荐zappa,它使 lambda 开发变得有趣。(并且它可以使用Asynchronous Task Execution在同一代码中轻松分块代码段)


推荐阅读