python - 尝试使用/Lambda 在 S3 上拆分大型 TSV 文件

标签 python amazon-web-services amazon-s3 aws-lambda

目标 我有一个数据生成过程,它在 S3 上创建一个大型 TSV 文件(大小在 30-40 GB 之间)。由于我想对其进行一些数据处理,因此更容易将其放入许多较小的文件(大小约为 1 GB 或更小)中。不幸的是,我没有太多能力来更改原始数据生成过程以在创建时对文件进行分区,因此我尝试创建一个简单的 lambda 来为我完成此操作,我的尝试如下

import json
import boto3
import codecs

def lambda_handler(event, context):
    s3 = boto3.client('s3')
    read_bucket_name = 'some-bucket'
    write_bucket_name = 'some-other-bucket'

    original_key = 'some-s3-file-key'
    obj = s3.get_object(Bucket=read_bucket_name, Key=original_key)

    lines = []
    line_count = 0
    file_count = 0
    MAX_LINE_COUNT = 500000

    def create_split_name(file_count):
        return f'{original_key}-{file_count}'

    def create_body(lines):
        return ''.join(lines)

    for ln in codecs.getreader('utf-8')(obj['Body']):
        if line_count > MAX_LINE_COUNT:
            key = create_split_name(file_count)

            s3.put_object(
                Bucket=write_bucket_name,
                Key=key,
                Body=create_body(lines)
            )

            lines = []
            line_count = 0
            file_count += 1

        lines.append(ln)
        line_count += 1

    if len(lines) > 0:
        file_count += 1
        key = create_split_name(file_count)
        s3.put_object(
                Bucket=write_bucket_name,
                Key=key,
                Body=create_body(lines)
        )

    return {
        'statusCode': 200,
        'body': { 'file_count': file_count }
    }

这在功能上很有效,但问题在于文件足够大,无法在 AWS lambda 的 15 分钟运行窗口内完成。所以我的问题是这些

  1. 能否以任何明显的方式优化此代码以减少运行时间(我不是分析 lambda 代码的专家)?
  2. 将其移植到编译语言会给运行时带来任何真正的好处吗?
  3. AWS 中是否还有其他实用程序可以解决此问题? (这里简单说明一下,我知道我可以启动 EC2 服务器来为我执行此操作,但理想情况下我正在尝试找到无服务器解决方案)

更新我尝试过的另一个选择是不拆分文件,而是告诉不同的 lambda 作业使用 Range 简单地读取同一文件的不同部分。

我可以尝试通过执行以下操作来读取文件

obj = s3.get_object(Bucket='cradle-smorgasbord-drop', Key=key, Range=bytes_range)
lines = [line for line in codecs.getreader('utf-8')(obj['Body'])]

但是,在大约 30 GB 的文件中,我的 bytes_range=0-49999999 这只是前 50 MB,而且下载时间比我想象的要长得多。 (实际上我还没看到它完成)

最佳答案

为了避免达到 AWS Lambda 函数执行 15 分钟的限制,您必须确保从 S3 读取的数据量与您在 15 分钟或更短的时间内可以处理的数据量相同。

您可以在 15 分钟或更短的时间内处理来自 S3 的数据量取决于您的函数逻辑以及 AWS Lambda 函数的 CPU 和网络性能。 AWS Lambda 函数的可用 CPU 性能随着提供给 AWS Lambda 函数的内存而扩展。来自 AWS Lambda documentation :

Lambda allocates CPU power linearly in proportion to the amount of memory configured. At 1,792 MB, a function has the equivalent of one full vCPU (one vCPU-second of credits per second).

因此,第一步您可以尝试增加提供的内存,看看这是否会增加您的函数在 15 分钟内可以处理的数据量。

提高 AWS Lambda 函数的 CPU 性能可能已经解决了您现在的问题,但如果您将来必须处理更大的文件,它就无法很好地扩展。

幸运的是,有一个解决方案:从 S3 读取对象时,您不必一次读取整个对象,但可以使用范围请求仅读取对象的一部分。为此,您所要做的就是在调用 get_object() 时指定要读取的范围。来自 boto3 documentation for get_object() :

Range (string) -- Downloads the specified range bytes of an object. For more information about the HTTP Range header, go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

在您的情况下,您不是为 S3 中的每个对象触发一次 AWS Lambda 函数来处理,而是为同一对象多次触发它,但处理该对象的不同 block 。根据您调用函数的方式,您可能需要另一个 AWS Lambda 函数来检查 S3 中要处理的对象的大小(使用 head_object() )并为每个数据 block 触发一次实际的 Lambda 函数。

虽然您需要额外的分块逻辑,但您不再需要在原始 AWS Lambda 函数中拆分读取数据,因为您只需确保每个 block 的大小为 1GB,并且仅包含属于该 block 的数据由于范围请求而被读取。当您为每个 block 调用单独的 AWS Lambda 函数时,您还可以并行化当前的顺序逻辑,从而提高执行速度。

最后,您可以通过不将整个数据读入内存,而是使用流式传输来大幅减少 AWS Lambda 函数消耗的内存量。

关于python - 尝试使用/Lambda 在 S3 上拆分大型 TSV 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59108434/

相关文章:

python - 使用 Python urllib2 将 zip 文件直接上传到 AWS S3

amazon - 使用 Uploadify 直接发布到 Amazon S3

python - 把字典变成列表,排序然后返回表

django - 经验丰富的 Web 开发人员如何将 Django 部署到 EC2 上的生产环境中?

python - 从excel中提取多个表

amazon-web-services - 我可以在 aws 安全组中添加 dns 名称吗

python - 无法从 aws lambda 层导入模块

java - AmazonS3ClientBuilder 问题

python - Win 64bit GetThreadContext 返回 zeroe'd out 寄存器,或 0x57 错误代码

python - 使用 matplotlib 将一些文本显示为粗体