繁体   English   中英

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

[英]Trying to split a large TSV file on S3 w/ 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 文档

Lambda 会与配置的内存量成比例地线性分配 CPU 功率。 一个函数大小为 1,792 MB,相当于一个完整的 vCPU(每秒一个 vCPU 的积分)。

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

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

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

Range ( string ) -- 下载对象的指定范围字节。 有关 HTTP 范围标头的更多信息,请访问http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM