You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无内存溢出(OOM)的情况下实现S3桶间文件复制

解决S3桶同步时的内存溢出问题

你的问题核心是把两个桶的所有对象Key都存在列表里,当真实数据量很大时,这两个列表会占用大量内存,直接触发OOM。你已经在用list_objects_v2了(这是S3官方推荐的列表接口,比旧的list_objects更可靠),不用换API,只需要优化内存使用逻辑。

优化思路

  1. 用集合替代列表存储目标桶的Key:集合的查找效率是O(1),比列表的O(n)快得多,同时内存占用也比列表更紧凑。
  2. 遍历源桶对象时实时判断+即时复制:不需要存储源桶的所有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 08:25:01