S3 版本化在同一个键下保留多个版本,只要所需版本仍在,就能恢复许多误覆盖和普通删除。它并不防止所有删除,也不会独立构成备份。真正能否恢复应用,取决于保留策略、访问权限、加密密钥是否仍然可用,以及恢复程序是否经过验证。版本存在与业务恢复成功之间,还有需要明确检查的步骤。
分清版本化状态
在启用版本化的桶中,PUT 产生新版本 ID,同时保留之前的数据版本。每个版本存储完整对象,并非只记录变化的字节。因此,反复覆盖一个大对象,即使可见键数量保持不变,存储成本也可能持续增长。这与块级增量快照的空间模型不同,不能只用修改了多少字节估算对象版本的费用。
启用之前已存在的对象保留 null 版本 ID,新产生的版本化写入不会覆盖那个旧版本。一旦启用,桶就不能回到从未版本化的状态,但可以暂停版本化。暂停期间,后续写入使用 null 版本槽位,可能替换已有 null 版本。讨论覆盖是否安全时,必须明确桶当前处于哪一种状态,不能把暂停与启用混为一谈。
null 是真正的版本标识,不是永久保留保证。null 版本变成非当前版本后,可以受到非当前版本过期规则影响;在授权允许时,也能够按明确版本 ID 删除。因此,列表中出现 null 并不能证明那份旧数据不参与生命周期清理,更不能把它当成永远不会消失的恢复基线。
删除标记改变可见性
启用版本化时,不带版本 ID 的普通 DELETE 创建删除标记。标记处于当前位置后,不指定版本的 GET 通常返回 404;显式请求删除标记的版本 ID 则返回 405。较早数据仍可通过其数据版本 ID 获取,但还受到访问权限、存储类别及加密条件影响。看见 404 不能直接推断底下所有版本已经消失。
删除标记没有对象载荷,却不是免费零字节条目,S3 会按它保存的键名字节收费。删除那个明确的当前标记,可以暴露前一版本,但前一版本本身也可能仍是标记。另一种恢复方式是复制明确的历史数据版本,生成新的当前数据版本,同时保留历史。标记没有数据,不能把它当成普通对象复制来恢复。
按指定 ID 删除数据版本,是另一种永久操作,版本化本身不能把被永久删除的版本找回来。配置 MFA Delete 后,永久删除版本和改变版本化状态会增加相应认证要求,但开启版本化并不会自动开启这种保护。AWS 不支持将 MFA Delete 与生命周期配置一起使用,不能假设所有保护开关可以无条件同时启用。
生命周期表达的是保留取舍
当前版本 Expiration 与 NoncurrentVersionExpiration 各自处理不同对象。启用版本化时,当前数据过期通常创建标记,并把数据变为非当前版本;非当前版本过期则永久删除符合条件的旧数据。资格按成为非当前版本后的时间计算,可选的保留版本数量还会增加另一项条件,并不是简单比较最初上传时间。
生命周期操作异步执行。配置三十天规则,不能证明任何时刻都精确保留三十份版本。过滤范围、写入频率、保留数量、复制状态和 Object Lock 都可能影响资格。转入冷存储保留数据,但会改变存储、取回和最短保存期限等费用。应结合真实对象大小和恢复需求计算,不能只比较表面上的每字节存储单价。
坏掉的样子
普通列表保持稳定时,非当前版本字节仍可能增长并推高费用。应检查版本清单与生命周期实际覆盖范围,而不是期待一个固定的版本数量比率可以诊断所有情况。桶不必像本地磁盘那样物理写满,成本就可能已经失控;生命周期也不是硬性预算上限,更不是唯一可能的保留控制办法。
规则过于激进则会产生相反问题:真正需要恢复的旧版本已经被永久删除。保留窗口应覆盖合理的最迟发现时间和恢复要求。HEAD 能检查元数据,但不会下载并验证对象内容,成功的元数据请求不能当作完成恢复测试。应确认目标版本存在、可读,并且其字节及业务内容确实符合预期。
快速验证
用 Python 3 运行下面模型,保持断言开启。保留的历史执行记录覆盖这些完全相同的模型字节;本次来源复核没有重跑。它完全在内存中表示启用版本化的版本栈,其中包含一份原有 null 数据版本。不需要 AWS 账户、凭据或远程对象。删除标记的计费、真实时间、权限、生命周期调度以及并发写入,都明确不在这个简化模型范围内。
versions = [("null", b"old"), ("v1", b"new")]
marker = ("d1", None)
versions.append(marker)
def get_current():
payload = versions[-1][1]
return (404, None) if payload is None else (200, payload)
assert get_current() == (404, None)
assert sum(len(data) for _, data in versions if data is not None) == 6
versions.remove(marker)
assert get_current() == (200, b"new")
print("remove current marker: previous data visible")
versions.append(("d2", None))
# Copy an explicit data version to create a new current version.
old = next(data for identifier, data in versions if identifier == "null")
versions.append(("v2", old))
assert get_current() == (200, b"old")
assert any(identifier == "d2" for identifier, _ in versions)
print("copy explicit data version: new current version; history retained")
# Noncurrent null versions are not inherently protected from deletion.
versions[:] = [entry for entry in versions if entry[0] != "null"]
assert get_current() == (200, b"old")
print("remove old null version: current copy remains")
三行输出分别表示暴露上一版本、复制指定历史数据,以及删除非当前 null 版本。创建标记时数据载荷字节仍保留,但这个计数没有包含标记需要收费的键名字节,不能当作真实账单预测。模型也不为生命周期保留数量给出数字保证,因为那需要时间、过滤和其他条件一起参与判断。
真实恢复时,应完整处理分页的数据版本和删除标记列表,并筛选准确键名,避免把同前缀其他对象混进去。选定明确的数据版本之后,取回并验证内容,再决定是否改变生产当前状态。读取历史、验证业务数据、重新发布当前版本是不同动作,需要分别记录结果,不能用一条列表命令代替整套恢复演练。
来源
- AWS:S3 版本化,版本化与 null 版本如何工作,已核验 2026-09-16。
- AWS:S3 删除标记,删除标记做什么、如何撤销,已核验 2026-09-16。
- AWS:S3 生命周期管理,当前与非当前版本的过期和迁移规则,已核验 2026-09-16。
- AWS 对象过期,资格条件、永久删除与异步执行,已核验 2026-10-15。
- AWS MFA Delete,可选保护及生命周期约束,已核验 2026-10-15。
- AWS 恢复旧版本,显式版本复制与恢复,已核验 2026-10-15。