我正在尝试将大约 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 瓶颈的两种可能解决方案:
无论哪种方式,一旦您拥有对象列表,您就可以让您的代码读取 list (例如,如果您可以下载和存储文件,则可以从本地存储(例如本地磁盘)中读取,或者甚至只需发送一系列 ListObjects 和 GetObject请求 S3 检索库存),然后在决定要复制哪些对象和新对象键(即您的逻辑)之后启动一堆工作线程并对对象运行 S3 复制对象操作。
简而言之:
这里需要注意的一件事是,如果您启动了数量惊人的工作人员,并且他们最终都访问了完全相同的 S3 分区以获取副本。在这种情况下,您最终可能会收到来自 S3 的一些错误。为了降低发生这种情况的可能性,您可以执行以下操作:
最后说明:还有另一件事需要考虑,即在复制操作期间是否可以修改存储桶。如果它可以被修改,那么您将需要一种策略来处理可能因为未列出而无法复制的对象,或者处理由您的代码复制但从源中删除的对象。
关于amazon-web-services - 复制 S3 文件的更快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62524119/