amazon-web-services - 复制 S3 文件的更快方法

标签 amazon-web-services amazon-s3 boto3 aws-cli boto

我正在尝试将大约 5000 万个文件和 15TB 的总大小从一个 s3 存储桶复制到另一个存储桶。
有 AWS CLI 选项可以快速复制。但就我而言,我想放置一个过滤器和日期范围。于是想到用boto3写代码。
源桶输入结构:

Folder1
    File1 - Date1
    File2 - Date1
Folder2
    File1 - Date2
    File2 - Date2
Folder3
    File1_Number1 - Date3
    File2_Number1 - Date3
Folder4
    File1_Number1 - Date2
    File2_Number1 - Date2
Folder5
    File1_Number2 - Date4
    File2_Number2 - Date4

因此,目的是使用日期范围(Date2 到 Date4)从每个文件夹中复制以“File1”开头的所有文件。 date(Date1, Date2, Date3, Date4) 是文件修改日期。
输出将具有日期键分区,我使用 UUID 来保持每个文件名的唯一性,因此它永远不会替换现有文件。因此,具有相同日期(文件的修改日期)的文件将位于同一文件夹中。
目标桶会有输出:
Date2
    File1_UUID1
    File1_Number1_UUID2
Date3
    File1_Number1_UUID3
Date4
    File1_Number2_UUID4

我已经使用 boto3 API 和 AWS 胶水编写了代码来运行代码。但是 boto3 API 每天复制 50 万个文件。
编码:
s3 = boto3.resource('s3', region_name='us-east-2', config=boto_config)

# source and target bucket names
src_bucket_name = 'staging1'
trg_bucket_name = 'staging2'

# source and target bucket pointers
s3_src_bucket = s3.Bucket(src_bucket_name)
print('Source Bucket Name : {0}'.format(s3_src_bucket.name))
s3_trg_bucket = s3.Bucket(trg_bucket_name)
print('Target Bucket Name : {0}'.format(s3_trg_bucket.name))

# source and target directories
trg_dir = 'api/requests'

# source objects
s3_src_bucket_objs = s3_src_bucket.objects.all()

# Request file name prefix
file_prefix = 'File1'

# filter - start and end date
start_date = datetime.datetime.strptime("2019-01-01", "%Y-%m-%d").replace(tzinfo=None)
end_date = datetime.datetime.strptime("2020-06-15", "%Y-%m-%d").replace(tzinfo=None)

# iterates each source directory
for iterator_obj in s3_src_bucket_objs:
    file_path_key = iterator_obj.key
    date_key = iterator_obj.last_modified.replace(tzinfo=None)
    if start_date <= date_key <= end_date and file_prefix in file_path_key:
        # file name. It start with value of file_prefix.
        uni_uuid = uuid.uuid4()
        src_file_name = '{}_{}'.format(file_path_key.split('/')[-1], uni_uuid)

        # construct target directory path
        trg_dir_path = '{0}/datekey={1}'.format(trg_dir, date_key.date())

        # source file
        src_file_ref = {
            'Bucket': src_bucket_name,
            'Key': file_path_key
        }

        # target file path
        trg_file_path = '{0}/{1}'.format(trg_dir_path, src_file_name)

        # copy source file to target
        trg_new_obj = s3_trg_bucket.Object(trg_file_path)

        trg_new_obj.copy(src_file_ref, ExtraArgs=extra_args, Config=transfer_config)

# happy ending
我们是否有任何其他方法可以使其快速或任何其他方法来复制此类目标结构中的文件?您对改进代码有什么建议吗?我正在寻找一些更快的方式来复制文件。您的意见将是有值(value)的。谢谢!

最佳答案

每天只能复制 500k 个对象的最可能原因(因此复制 50M 个对象需要大约 3-4 个月,这绝对不合理)是因为 您正在按顺序进行操作。
您的代码运行的大部分时间都花在等待 S3 复制对象请求发送到 S3,由 S3 处理(即复制对象),然后将响应发送回给您。平均而言,每个对象大约需要 160 毫秒(50 万/天 == 大约每 160 毫秒 1 个),这是合理的。
要显着提高复制操作的性能,您应该简单地将其并行化:让多个线程同时运行副本。
一旦 Copy 命令不再是瓶颈(即,在您让它们同时运行之后),你会遇到另一个瓶颈:列表对象请求 .此请求按顺序运行,每页最多只返回 1k 个键,因此您最终必须使用简单、简单的代码按顺序发送大约 50k 个 List Object 请求(此处,“naive” == list 没有任何前缀或分隔符,等待响应,并使用提供的下一个继续 token 再次列出以获取下一页)。
ListObjects 瓶颈的两种可能解决方案:

  • 如果您非常了解您的存储桶的结构(即“文件夹的名称”,这些“文件夹”中"file"分布的统计信息等),您可以尝试通过使每个线程并行化 ListObjects 请求列出给定的前缀。请注意,这不是通用的解决方案,需要对存储桶的结构有深入的了解,并且通常只有在最初计划存储桶的结构以支持这种操作时才能很好地工作。
  • 或者,您可以要求 S3 生成存储桶的 list 。您最多需要等待 1 天,但最终会得到 CSV 文件(或 ORC 或 Parquet),其中包含有关存储桶中所有对象的信息。

  • 无论哪种方式,一旦您拥有对象列表,您就可以让您的代码读取 list (例如,如果您可以下载和存储文件,则可以从本地存储(例如本地磁盘)中读取,或者甚至只需发送一系列 ListObjects 和 GetObject请求 S3 检索库存),然后在决定要复制哪些对象和新对象键(即您的逻辑)之后启动一堆工作线程并对对象运行 S3 复制对象操作。
    简而言之:
  • 首先获取所有对象的列表;
  • 然后启动许多 worker 来运行副本。

  • 这里需要注意的一件事是,如果您启动了数量惊人的工作人员,并且他们最终都访问了完全相同的 S3 分区以获取副本。在这种情况下,您最终可能会收到来自 S3 的一些错误。为了降低发生这种情况的可能性,您可以执行以下操作:
  • 您可以将其随机化,而不是按顺序遍历您的对象列表。例如,加载库存,将项目以随机顺序放入队列,然后让您的工作人员从该队列中消费。这将降低单个 S3 分区过热的可能性
  • 保持您的工作人员不超过几百个(单个 S3 分区应该能够轻松地处理每秒数百个请求)。

  • 最后说明:还有另一件事需要考虑,即在复制操作期间是否可以修改存储桶。如果它可以被修改,那么您将需要一种策略来处理可能因为未列出而无法复制的对象,或者处理由您的代码复制但从源中删除的对象。

    关于amazon-web-services - 复制 S3 文件的更快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62524119/

    相关文章:

    amazon-web-services - 删除后 Kubernetes 集群仍在运行

    java - AWS - 使用 Java 访问 s3 存储桶中文件夹中的文件

    android - 使用 ImageLoader 从 AWS url android 下载图像

    python - 授予 AWS Lambda 列出所有 SNS 主题的权限

    amazon-web-services - 在 Boto3 中创建权限的问题

    amazon-web-services - 如何防止EMR Spark步骤重试?

    python - AWS CLI Errno 13 权限被拒绝,无法使用 aws configure 命令设置凭证

    amazon-web-services - 如何下载一个S3 bucket的所有内容,然后上传到另一个bucket?

    python - Django 存储 - 无法加载亚马逊的 S3 绑定(bind)错误

    python - 使用 Paramiko 将文件从 SFTP 传输到 S3