如何在无内存溢出(OOM)的情况下实现S3桶间文件复制
解决S3桶同步时的内存溢出问题
你的问题核心是把两个桶的所有对象Key都存在列表里,当真实数据量很大时,这两个列表会占用大量内存,直接触发OOM。你已经在用list_objects_v2了(这是S3官方推荐的列表接口,比旧的list_objects更可靠),不用换API,只需要优化内存使用逻辑。
优化思路
- 用集合替代列表存储目标桶的Key:集合的查找效率是O(1),比列表的O(n)快得多,同时内存占用也比列表更紧凑。
- 遍历源桶对象时实时判断+即时复制:不需要存储源桶的所有Key,遍历到一个对象就检查是否在目标桶集合里,不在就直接复制,用完即丢弃,大幅降低内存占用。
优化后的代码
import boto3 # 初始化S3客户端 s3_client = boto3.client('s3') bucket1 = 'bucket1_name' bucket2 = 'bucket2_name' prefix = 'test' # 第一步:把目标桶的所有非文件夹Key存入集合(内存友好,查找快) bucket2_keys = set() paginator = s3_client.get_paginator("list_objects_v2") for page in paginator.paginate(Bucket=bucket2, Prefix=prefix): if 'Contents' not in page: continue for obj in page['Contents']: key = obj['Key'] if not key.endswith("/"): bucket2_keys.add(key) # 第二步:遍历源桶对象,实时检查并复制缺失的文件 for page in paginator.paginate(Bucket=bucket1, Prefix=prefix): if 'Contents' not in page: continue for obj in page['Contents']: key = obj['Key'] if not key.endswith("/") and key not in bucket2_keys: # 直接复制,无需存储所有待复制Key s3_client.copy_object( Bucket=bucket2, Key=key, CopySource={'Bucket': bucket1, 'Key': key} )
极端场景的终极优化(超大规模对象)
如果目标桶的对象数量多到集合都存不下(比如百万级以上),可以放弃预存所有Key,改为遍历源桶时实时检查目标桶是否存在该对象:
import boto3 from botocore.exceptions import ClientError s3_client = boto3.client('s3') bucket1 = 'bucket1_name' bucket2 = 'bucket2_name' prefix = 'test' paginator = s3_client.get_paginator("list_objects_v2") for page in paginator.paginate(Bucket=bucket1, Prefix=prefix): if 'Contents' not in page: continue for obj in page['Contents']: key = obj['Key'] if key.endswith("/"): continue # 实时检查目标桶是否存在该Key try: s3_client.head_object(Bucket=bucket2, Key=key) except ClientError as e: # 404表示不存在,执行复制 if e.response['Error']['Code'] == '404': s3_client.copy_object( Bucket=bucket2, Key=key, CopySource={'Bucket': bucket1, 'Key': key} ) # 其他错误按需处理 else: raise
这种方式内存占用几乎为0,但每个对象都要发一次head_object请求,速度会慢一些,适合内存资源紧张的场景。
额外注意点
- 原代码里
s3(resource)和s3_client混用,建议统一用client,操作更直接,内存占用也更低。 - 处理分页时要判断
Contents是否存在,避免空列表报错。
内容的提问来源于stack exchange,提问作者Jennifer Crosby
相关产品推荐
相关产品推荐

