一个对象写入 S3 后,AWS 承诺它的持久性是 99.999999999%(11 个 9)——这意味着如果你存了 100 亿个对象,统计上一年才可能丢一个。与此同时,S3 的容量几乎没有上限,单个桶可以存放无限数量的对象,总存储量已经超过百万亿(数百 EB)级别。一个分布式存储系统如何同时做到”几乎不丢数据”和”几乎无限容量”?靠的不是单一技术,而是元数据层、索引层、存储层三层架构的协同,加上跨可用区(Availability Zone,AZ)复制、纠删码(Erasure Coding)、持续数据校验等机制的叠加。
本文从 S3 的外部行为和 AWS 公开的架构信息出发,拆解云对象存储的内部架构设计——分层如何划分、持久性如何保障、一致性如何演进、存储类别在实现上有什么差异、成本模型背后的工程逻辑是什么。所有分析基于 AWS 官方文档、re:Invent 演讲、公开论文和工程经验推断,非一手源码分析,文中会明确标注推断部分。
一、对象存储的核心设计目标
1.1 与块存储、文件存储的本质区别
对象存储不是块存储加一层 HTTP 接口,也不是文件系统去掉目录树。三者的设计目标从根本上不同:
| 维度 | 块存储(EBS) | 文件存储(EFS/NFS) | 对象存储(S3) |
|---|---|---|---|
| 访问接口 | 块设备(/dev/xvd*) | POSIX 文件系统 | HTTP REST API |
| 寻址方式 | LBA(逻辑块地址) | 路径名(/dir/file) | 桶名 + 键名(bucket/key) |
| 修改方式 | 原地覆写任意偏移 | 原地覆写任意偏移 | 整对象替换(不支持部分修改) |
| 一致性粒度 | 单块 | 单文件(close-to-open) | 单对象(强一致) |
| 扩展瓶颈 | 单卷容量有限(64 TiB) | 元数据服务器 | 几乎无限(分片扩展) |
| 延迟 | 亚毫秒 | 毫秒级 | 数十到数百毫秒 |
| 典型用途 | 数据库、OS 根盘 | 共享文件、HOME 目录 | 备份、数据湖、静态资源 |
对象存储为什么能做到”几乎无限容量”?关键在于它放弃了两个约束:第一,放弃原地修改——对象是不可变的(immutable),修改就是重新写一个完整对象;第二,放弃
POSIX 语义——没有目录树、没有文件锁、没有
seek(),键名空间是扁平的哈希映射。这两个放弃换来的是:元数据可以独立分片扩展,数据可以用纠删码分散到任意多节点,容量扩展不受单机或单卷限制。
1.2 S3 的设计约束
理解 S3 的架构设计,先理解它要满足的约束条件:
- 持久性优先于可用性。 11 个 9 的持久性(数据不丢)比 4 个 9 的可用性(服务可访问)优先级更高。宁可暂时不可用,也不能丢数据。
- 吞吐优先于延迟。 单次 GET/PUT 延迟在 100-200 毫秒级别,不追求亚毫秒。但支持大规模并发——单桶可以支撑每秒数千到数万请求。
- 容量近似无限。 不需要预先分配空间,不需要扩容操作,按用量计费。
- 最终一致到强一致。 2020 年 12 月之前是最终一致性,之后所有标准操作都是读后写强一致。
这些约束直接决定了架构选择:分层解耦、纠删码而非副本、跨 AZ 同步复制而非异步。
二、三层架构:元数据、索引与存储
S3 的内部架构可以拆解为三个逻辑层:元数据层(Metadata Tier)、索引层(Index Tier)和存储层(Storage Tier)。这不是猜测——AWS 在多次 re:Invent 演讲(如 2021 年 Andy Warfield 的 “Diving Deep on S3 Consistency”、2023 年 “S3 Internals” 等)中公开了这个分层模型。
graph TB
subgraph 客户端
A[应用 / SDK]
end
subgraph S3 前端
B[S3 API 网关<br/>请求路由 / 认证 / 限流]
end
subgraph 元数据层
C1[桶元数据<br/>ACL / 版本策略 / 生命周期]
C2[对象元数据<br/>键名 → 存储位置映射]
end
subgraph 索引层
D[分布式键值索引<br/>键名查找 / 版本管理 / 列举]
end
subgraph 存储层
E1[AZ-1 存储节点<br/>纠删码分片]
E2[AZ-2 存储节点<br/>纠删码分片]
E3[AZ-3 存储节点<br/>纠删码分片]
end
A --> B
B --> C1
B --> C2
C2 --> D
D --> E1
D --> E2
D --> E3
上图展示了 S3 的分层结构:客户端请求经过 API 网关后,先经元数据层查询桶配置和对象位置信息,再通过索引层定位对象的物理存储分片,最后从存储层的多个 AZ 读取或写入纠删码分片。下面逐层拆解。
2.1 元数据层
元数据层负责两件事:管理桶级别的配置信息,以及维护对象键名到物理存储位置的映射。
桶元数据 包括桶名、所属区域(Region)、访问控制策略(ACL / Bucket Policy)、版本控制配置、生命周期规则、加密配置、事件通知配置等。桶元数据的读写频率远低于对象操作,但对一致性要求很高——一个桶策略的修改必须立即生效,不能出现”策略已更新但还能用旧策略访问”的窗口。
对象元数据 是更关键的部分。每个对象的元数据至少包括:
- 键名(Key)
- 版本 ID(Version ID,启用版本控制时)
- 对象大小
- ETag(内容哈希,通常是 MD5 或分段上传的组合哈希)
- 存储类别(Standard / IA / Glacier 等)
- 用户自定义元数据(x-amz-meta-*)
- 服务端加密信息
- 数据分片的物理位置映射(哪些分片在哪些存储节点上)
对象元数据的规模和对象数量成正比——S3 存储了超过 350 万亿个对象(2024 年 re:Invent 数据),元数据本身的存储和查询就是一个巨大的分布式系统问题。
元数据层的工程挑战在于:每个对象至少有一条元数据记录,假设每条记录 1 KB(键名、版本信息、位置映射等),350 万亿个对象的元数据就是 350 PB——这本身就是一个需要分布式存储的数据集。元数据层必须自己也做分片、复制和故障恢复,形成了一个”存储系统的存储系统”的递归结构。
元数据层还承担了一个容易被忽略的职责:垃圾回收。 当对象被删除或被新版本覆盖时,旧数据的存储分片不能立即物理删除——可能还有正在进行的读取操作引用这些分片。元数据层需要维护引用计数或标记-清扫机制,在确认没有活跃引用后才安排物理删除。在万亿级对象规模下,垃圾回收本身就是一个持续运行的大规模后台任务。
2.2 索引层
索引层解决的核心问题是:给定一个桶名和键名,快速找到这个对象的元数据和数据位置。
S3 的索引不是一棵全局的 B 树或一个全局的哈希表——在万亿级对象规模下,任何单一的索引结构都无法支撑。索引按照桶进行分区,每个桶的键空间被进一步分片(Shard)。分片策略的核心是按键名的哈希或字典序范围做水平切分,使得每个分片管理的键数量在可控范围内。
索引层需要支撑三类操作:
- 点查(Point Lookup)。
GetObject和HeadObject需要按桶名+键名精确查找。延迟要求最高,通常在个位毫秒级完成。 - 列举(List)。
ListObjectsV2按前缀和字典序列举键名。这要求索引支持范围扫描,不能只用纯哈希。S3 的列举是按字典序排列的,返回结果按键名排序。 - 版本管理。 启用版本控制后,同一个键名可以有多个版本。索引需要维护版本链,支持按版本 ID 查询和按时间顺序列举版本。
索引层的分片和扩展是 S3 能够支撑高并发的关键。AWS 文档提到单前缀支撑每秒 5500 次 GET 和 3500 次 PUT——这实际上反映了单个索引分片的吞吐上限。使用多前缀(多分片)可以线性提升总吞吐。
索引层的另一个重要职责是支撑 S3 事件通知(Event Notification)。 当对象被创建、删除或还原时,索引层需要生成事件并投递到 SNS、SQS 或 Lambda。事件的生成必须和索引更新保持一致——如果索引已经更新但事件没有生成,下游的数据管道就会丢失触发信号。2020 年的强一致性改造让这个保证变得更可靠:PUT 返回成功意味着索引已更新、事件已生成(或即将生成),不会出现”对象已写入但 LIST 看不到、事件也没到”的情况。
2.4 写入路径的完整流程
把三层串起来,一次 PutObject
的内部流程大致如下:
sequenceDiagram
participant C as 客户端
participant GW as API 网关
participant IDX as 索引层
participant ST1 as 存储节点 AZ-1
participant ST2 as 存储节点 AZ-2
participant ST3 as 存储节点 AZ-3
C->>GW: PUT /bucket/key(数据 + 元数据)
GW->>GW: 认证、授权、请求校验
GW->>IDX: 分配存储位置、生成分片计划
IDX-->>GW: 返回分片目标节点列表
par 并行写入分片
GW->>ST1: 写入数据分片 1, 2, 5, 8
GW->>ST2: 写入数据分片 3, 4, 6 + 校验分片 1
GW->>ST3: 写入数据分片 7 + 校验分片 2, 3
end
ST1-->>GW: 写入确认
ST2-->>GW: 写入确认
ST3-->>GW: 写入确认
GW->>IDX: 提交元数据(键名→分片位置映射)
IDX-->>GW: 元数据提交成功
GW-->>C: HTTP 200 OK + ETag
上图展示了 PUT 操作的关键步骤:API 网关先从索引层获取分片计划,然后并行向多个 AZ 的存储节点写入纠删码分片,等待足够的写入确认后,将对象的元数据(键名到分片位置的映射)提交到索引层,最后向客户端返回成功。这里有一个关键细节:元数据提交是在数据写入完成之后——这保证了一旦 PUT 返回成功,后续的 GET 一定能通过索引找到并读取数据。
2.3 存储层
存储层负责数据的物理存储和持久性保障。这一层的设计围绕一个核心目标:11 个 9 的持久性。
数据写入存储层时,经过以下处理:
- 分片。 对象数据被切分为多个分片(Shard / Chunk)。
- 纠删码编码。 对数据分片计算纠删码校验分片。例如,一种常见的配置是将数据编码为 N 个数据分片 + M 个校验分片(如 8+3 或 6+3),只需要 N 个分片即可恢复完整数据,最多允许 M 个分片同时丢失。
- 跨 AZ 分布。 分片被分散存储到同一区域内的多个可用区。标准存储类别至少跨 3 个 AZ 存储。
- 写入确认。 只有当足够数量的分片被持久化写入(写入磁盘并确认)后,PUT 操作才向客户端返回成功。
存储层的每个节点管理本地磁盘上的分片数据。节点间不做传统意义上的主从复制——纠删码本身提供了冗余,不需要额外的副本。这也是对象存储相比三副本块存储更节省空间的原因:三副本的存储开销是 3 倍,而 8+3 纠删码的开销只有 1.375 倍。
三、持久性保障机制
3.1 11 个 9 是怎么算出来的
11 个 9 的持久性(99.999999999%)是一个概率声明:在一年时间内,任意一个对象丢失的概率小于 10^-11。要理解这个数字怎么达成,需要拆解持久性模型的输入参数。
持久性模型的核心参数:
- 分片数和校验分片数。 纠删码的 N+M 配置。M 越大,能容忍的同时故障越多,持久性越高。
- 单分片丢失概率。 取决于磁盘故障率(年化故障率 AFR 通常在 0.5%-2%)。
- 修复时间。 分片丢失后,系统重建新分片的时间。修复越快,多分片同时丢失的窗口越短。
- 故障相关性。 如果分片分布在不同 AZ、不同机架、不同服务器上,故障的相关性就低。同一块磁盘上的分片同时丢失概率为 1,不同 AZ 的磁盘同时故障概率极低。
简化的概率模型:假设纠删码配置为 8+3(8 数据分片 + 3 校验分片,共 11 分片),允许同时丢失 3 个分片。数据丢失需要在修复窗口内同时丢失 4 个或更多分片。如果单分片年故障率为 1%,分片分布在不同 AZ 的不同服务器上(故障独立),修复时间为数小时,那么在修复窗口内同时有 4 个分片故障的概率极低——量级上可以达到 10^-11 甚至更低。
实际的持久性模型比这复杂得多,还要考虑静默数据损坏(Bit Rot)、软件 Bug 导致的批量丢失、整个 AZ 不可用等相关故障场景。AWS 用持续审计(Continuous Auditing)来应对这些额外风险。
3.2 纠删码 vs. 三副本
| 维度 | 三副本 | 纠删码(8+3) |
|---|---|---|
| 存储开销 | 3 倍 | 1.375 倍 |
| 可容忍同时故障数 | 2(3 副本丢 2 个还有 1 个) | 3(11 分片丢 3 个还够 8 个恢复) |
| 读取时的计算开销 | 无(直接读副本) | 需要解码(如果部分分片不可用) |
| 修复开销 | 读一个完整副本,写一个新副本 | 读 N 个分片,计算并写入新分片 |
| 修复时的网络带宽 | 传输完整对象 | 传输部分分片(可以更分散) |
| 适用规模 | 小规模、低延迟场景 | 大规模、成本敏感场景 |
S3 在标准存储类别中使用纠删码而非三副本,核心原因是成本——在 EB 级存储规模下,三副本的存储开销不可接受。纠删码的代价是读取和修复时需要额外的计算,但对于对象存储的访问模式(大对象、高吞吐、非亚毫秒延迟需求),这个代价可以接受。
3.3 跨 AZ 复制策略
S3 Standard 存储类别承诺跨至少 3 个 AZ 存储数据。这里的”跨 AZ”不是简单地把三个副本各放一个 AZ,而是把纠删码的分片分散到多个 AZ。
graph LR
subgraph Region us-east-1
subgraph AZ-1
S1[分片 1]
S2[分片 2]
S5[分片 5]
S8[分片 8]
end
subgraph AZ-2
S3[分片 3]
S4[分片 4]
S6[分片 6]
S9[校验分片 1]
end
subgraph AZ-3
S7[分片 7]
S10[校验分片 2]
S11[校验分片 3]
end
end
上图是一种可能的分片分布示意(具体分布策略 AWS 未公开):8 个数据分片和 3 个校验分片被分散到 3 个 AZ。即使整个 AZ-3 不可用(丢失 3 个分片),剩余 8 个分片仍然足够恢复完整数据。
跨 AZ 分布策略的关键约束:
- 写入路径: PUT 操作需要等待足够分片写入成功才能返回。如果要求所有 11 个分片都写入成功才返回,延迟会被最慢的 AZ 拖住;如果只要求写入 N(8)个分片就返回,后台异步补齐剩余分片,则在异步补齐完成前持久性略低于目标值。实际实现需要在延迟和持久性之间取得平衡。
- 读取路径: 正常情况下只需要读取 N 个数据分片,不需要读校验分片。只有在某些分片不可用时才需要读取校验分片并做纠删码解码恢复。
- 修复路径: 检测到分片丢失后,系统从剩余分片重建新分片并放置到健康节点上。修复过程消耗网络带宽和计算资源,需要与正常读写流量竞争。
3.4 持续数据校验
纠删码能应对整块磁盘故障,但无法自动应对静默数据损坏——磁盘返回了错误数据但没有报告错误(Silent Data Corruption / Bit Rot)。S3 通过持续校验机制来发现和修复这类问题:
- 写入时校验。 对象写入时计算内容哈希(Content-MD5 或 SHA-256),写入完成后立即验证存储数据与原始哈希一致。
- 后台扫描。 存储节点持续扫描本地分片,验证校验和。发现损坏的分片会被标记为无效,触发从其他分片重建。
- 端到端校验。 读取时重新计算校验和并与存储的校验和比对,确保返回给客户端的数据是正确的。
AWS 在 2023 年的 re:Invent 演讲中提到,S3 每天运行数十亿次完整性校验。这种持续审计(Continuous Auditing)是 11 个 9 持久性的重要保障——不是只在写入时做一次校验就完事,而是在整个生命周期内持续验证。
3.5 故障域隔离
除了纠删码和数据校验,S3 的持久性还依赖于故障域(Failure Domain)的隔离设计。分片的放置不是随机的——系统会确保同一个对象的分片分布在不同的故障域中:
- 不同的物理服务器。 两个分片不会放在同一台服务器上,避免单机故障丢失多个分片。
- 不同的机架。 同一个 AZ 内的分片分布在不同机架上,避免交换机故障或电源故障导致批量丢失。
- 不同的 AZ。 跨 AZ 分布是最高级别的故障域隔离,应对整个数据中心不可用的场景。
故障域隔离的难点在于维护成本:当一个存储节点下线或新节点上线时,系统需要重新平衡分片分布,确保故障域隔离约束仍然满足。在 EB 级存储规模下,这种重平衡是一个持续运行的大规模编排任务。
3.6 防止软件 Bug 导致的批量丢失
硬件故障通常是局部的——一块磁盘坏了、一台服务器挂了。但软件 Bug 可能是全局的——一个有问题的代码版本部署到所有节点,可能同时损坏所有节点上的数据。这种相关故障(Correlated Failure)是纠删码无法应对的,因为纠删码假设分片故障是独立的。
AWS 采用的应对策略(基于公开信息推断):
- 灰度发布。 新代码版本分批部署,不同 AZ 的部署时间错开,出现问题时可以快速回滚。
- 写入校验和读取校验解耦。 写入路径和校验路径使用独立的代码路径,降低单个 Bug 同时影响写入和校验的概率。
- 多版本校验和。 使用多种哈希算法(如同时存储 MD5 和 SHA-256),即使一种算法的实现有 Bug,另一种仍然可以检测数据损坏。
四、一致性模型演进
4.1 最终一致性时代(2006-2020)
S3 在 2006 年发布时采用最终一致性(Eventual Consistency)模型。具体行为是:
- 新对象的 PUT 后 GET: 写后读一致(Read-after-Write Consistency)——PUT 成功后,立即 GET 同一键名可以获取到新写入的数据。但有一个例外:如果在 PUT 之前先做了 GET(返回 404),那么后续的 GET 可能仍然返回 404,因为 404 的结果可能被缓存。
- 覆盖 PUT 后 GET: 最终一致——覆盖一个已存在的对象后,GET 可能返回旧版本或新版本。
- DELETE 后 GET: 最终一致——删除一个对象后,GET 可能仍然返回已删除的对象。
- LIST: 最终一致——新写入的对象可能不会立即出现在列举结果中。
这个模型给应用开发带来了大量问题。一个典型场景:数据管道先写入一个对象,然后通知下游来读——下游可能读到旧版本。开发者不得不在应用层加入重试逻辑、版本检查或使用 DynamoDB 做外部一致性协调。
4.2 为什么最初选择最终一致性
最终一致性不是设计缺陷,而是在当时的技术约束下做出的工程取舍:
- 跨 AZ 复制的延迟。 强一致性要求写入操作等待所有 AZ 的索引更新完成才返回,增加写入延迟。
- 索引层的缓存。 索引查询结果被缓存以提高性能,缓存的存在天然导致读取可能看到过期数据。
- LIST 操作的复杂性。 列举操作需要扫描索引分片,分片之间的索引更新不是原子的,列举结果可能不反映最新写入。
说白了,在 2006 年的技术条件下,在万亿级对象规模上做跨 AZ 的强一致性索引更新,成本和复杂度都太高。
4.3 强一致性时代(2020 年 12 月至今)
2020 年 12 月,AWS 宣布 S3 支持所有操作的强一致性(Strong Consistency),即读后写一致性和列举一致性,且不额外收费、不牺牲性能。这是一次重大的架构升级。
升级后的行为:
- PUT 后 GET: 强一致。PUT 返回成功后,任何后续的 GET 都保证返回最新版本。
- PUT 后 LIST: 强一致。PUT 返回成功后,后续的 LIST 保证包含新写入的对象。
- DELETE 后 GET: 强一致。DELETE 返回成功后,后续的 GET 返回 404。
- DELETE 后 LIST: 强一致。DELETE 返回成功后,后续的 LIST 不包含已删除的对象。
4.4 强一致性的实现思路
AWS 首席工程师 Andy Warfield 在 2021 年 re:Invent 演讲 “Diving Deep on S3 Consistency” 中透露了实现思路。核心改造发生在索引层:
- 索引层引入”见证者”(Witness)机制。 每次写入操作不仅更新主索引分片,还通过一个分布式协议确保足够多的索引节点确认了更新。
- 读取路径增加一致性检查。 GET 和 LIST 操作在返回结果前,会检查结果的新鲜度——确认它反映了所有已确认的写入。
- 消除读取缓存的过期问题。 不是简单地关掉缓存,而是让缓存条目携带版本信息,读取时可以验证缓存是否足够新。
这里的技术核心是一种”缓存一致性”问题的解法。传统的缓存失效有两种策略:主动失效(写入时通知所有缓存节点)和被动失效(设置 TTL 过期)。前者在全球分布的节点间代价太高,后者无法保证强一致。S3 的做法更接近”乐观读 + 版本验证”:读取时先查本地缓存,但在返回前向权威索引节点验证版本号是否是最新的。如果版本号匹配,直接返回缓存结果(无需等待);如果不匹配,重新从权威节点读取。这样在大多数情况下(写入不频繁时)读取延迟不受影响,只有在写入刚发生时需要额外的验证往返。
这个改造的关键约束是:不能增加延迟、不能降低吞吐、不能额外收费。Andy Warfield 在演讲中强调,S3 团队花了数年时间做这个改造,重写了索引层的核心逻辑。
从工程角度看,这里有一个容易忽略的问题:强一致性让应用开发变简单了,但它不是免费的午餐——系统内部的协调开销增加了,只是 AWS 选择自己承担这个开销而不是转嫁给用户。在极高并发写入场景下,强一致性的索引更新可能成为吞吐瓶颈。AWS 通过索引分片的自动拆分和扩展来缓解这个问题。
五、存储类别的实现差异
S3 提供多种存储类别(Storage Class),价格和性能差异很大。这些差异不是简单地改一个参数——背后是不同的物理存储介质、不同的纠删码策略和不同的数据放置策略。
5.1 存储类别总览
| 存储类别 | 持久性 | 可用性 SLA | 最小存储期 | 首字节延迟 | 跨 AZ | 典型用途 |
|---|---|---|---|---|---|---|
| Standard | 11 个 9 | 99.99% | 无 | 毫秒级 | 3+ AZ | 频繁访问数据 |
| Intelligent-Tiering | 11 个 9 | 99.9% | 无 | 毫秒级 | 3+ AZ | 访问模式不确定 |
| Standard-IA | 11 个 9 | 99.9% | 30 天 | 毫秒级 | 3+ AZ | 不频繁访问 |
| One Zone-IA | 11 个 9 | 99.5% | 30 天 | 毫秒级 | 1 AZ | 可重建的低频数据 |
| Glacier Instant | 11 个 9 | 99.9% | 90 天 | 毫秒级 | 3+ AZ | 归档但需即时访问 |
| Glacier Flexible | 11 个 9 | 99.99% | 90 天 | 分钟到小时 | 3+ AZ | 归档 |
| Glacier Deep Archive | 11 个 9 | 99.99% | 180 天 | 12-48 小时 | 3+ AZ | 长期归档 |
5.2 Standard vs. Standard-IA:实现差异
Standard 和 Standard-IA(Infrequent Access,不频繁访问)的持久性相同(11 个 9),跨 AZ 策略相同,首字节延迟也相同。那么 IA 便宜在哪?贵在哪?
存储成本更低,访问成本更高。 Standard-IA 的每 GB 存储价格约为 Standard 的一半,但每次 GET 请求的价格是 Standard 的 10 倍(以 us-east-1 为例,Standard 的 GET 价格为 $0.0004/千次,IA 为 $0.001/千次),且有 128 KB 的最小计费对象大小。
从实现角度推断(AWS 未完全公开),IA 类别可能使用以下策略来降低存储成本:
- 更高的纠删码比例。 使用更多数据分片和更少校验分片(如 10+2 而非 8+3),降低存储开销。这不影响持久性计算(只要修复速度足够快),但在部分分片不可用时的读取成功率稍低,可能体现为略低的可用性 SLA(99.9% vs. 99.99%)。
- 更高密度的存储介质。 IA 数据可能优先放置在容量更大但吞吐更低的磁盘上,单位存储成本更低。
- 不同的缓存策略。 IA 对象的索引和数据可能不会被缓存在热存储层,读取时需要更多磁盘操作。
5.3 One Zone-IA:单 AZ 的代价
One Zone-IA 只在一个 AZ 内存储数据。存储价格比 Standard-IA 再低约 20%,但代价是:如果那个 AZ 发生物理灾难(如火灾、洪水),数据就丢了。
这不意味着 One Zone-IA 的持久性低——在 AZ 内部,纠删码和数据校验机制和 Standard 一样,磁盘故障导致数据丢失的概率同样极低。11 个 9 的持久性声明是在”AZ 正常运行”的前提下成立的。但 AZ 级别的灾难不在这个概率模型内。
One Zone-IA 适用于可以从其他来源重建的数据——例如从原始数据重新计算的中间结果、从其他区域复制过来的副本等。
5.4 Glacier 系列:从热到冷的存储介质变化
Glacier 的名字来源于”冰川”,暗示数据像冰川一样”冻住”——不经常移动,但非常持久。Glacier 系列的关键实现差异:
Glacier Instant Retrieval: 存储价格比 Standard-IA 低约 68%,但 GET 请求价格更高。首字节延迟仍然是毫秒级——说明数据仍然在在线磁盘上,只是通过更高的纠删码比例和更高密度的存储介质来降低成本。
Glacier Flexible Retrieval(原 Glacier): 首字节延迟在分钟到小时级别。这意味着数据不在随时可读的在线存储上。AWS 未公开 Glacier 的具体存储介质,但业界普遍推断使用了以下一种或多种策略:
- 数据存储在离线或近线磁带(Tape)上
- 数据存储在断电的磁盘阵列上,需要通电和初始化才能读取
- 数据存储在低功耗、高延迟的存储设备上
不同的取回选项对应不同的处理优先级:
- Expedited(加急):1-5 分钟,价格最高
- Standard(标准):3-5 小时
- Bulk(批量):5-12 小时,价格最低
取回操作的内部流程是异步的:调用
RestoreObject API 发起取回请求,S3
在后台从冷存储介质读取数据并写入在线存储层,完成后对象在指定天数内可通过标准
GET 读取。
# 从 Glacier Flexible 取回对象,标准优先级,取回后保留 7 天
aws s3api restore-object \
--bucket my-archive-bucket \
--key "backup/2024-01-full.tar.gz" \
--restore-request '{"Days": 7, "GlacierJobParameters": {"Tier": "Standard"}}'
# 查看取回状态
aws s3api head-object \
--bucket my-archive-bucket \
--key "backup/2024-01-full.tar.gz" \
--query "Restore"
# 输出示例:ongoing-request="false", expiry-date="Sun, 27 Oct 2025 00:00:00 GMT"Glacier Deep Archive: 首字节延迟 12-48 小时,存储价格最低(约 $0.00099/GB/月,是 Standard 的约 1/23)。这一级别几乎可以确定使用了磁带存储——只有磁带才能在如此低的成本下提供 11 个 9 的持久性。磁带库的写入是顺序的、批量的,读取需要装载磁带、定位数据,延迟在小时级别完全合理。
关于磁带存储的工程特点:现代磁带技术(如 LTO-9)单盘容量 18 TB(压缩后 45 TB),顺序读写速度 400 MB/s,持久性可达 30 年以上。磁带不耗电(只在读写时才需要驱动器),是长期归档的理想介质。但磁带的随机访问延迟极高(分钟级的装带+定位),完全不适合在线访问。这正是 Glacier Deep Archive 取回延迟长达 12-48 小时的原因——不是技术上做不到更快,而是批量调度磁带读取比逐个处理更经济。
5.5 Intelligent-Tiering:自动分层
Intelligent-Tiering 不是一种独立的存储介质方案,而是一个自动化的分层策略。它监控每个对象的访问频率,自动在多个层之间移动:
- 频繁访问层(Frequent Access Tier):等同于 Standard
- 不频繁访问层(Infrequent Access Tier):30 天未访问自动转入
- 归档即时访问层(Archive Instant Access Tier):90 天未访问自动转入
- 归档访问层(Archive Access Tier):可选,90-730 天
- 深度归档访问层(Deep Archive Access Tier):可选,180-730 天
Intelligent-Tiering 的代价是每个对象每月有一笔监控和自动化费用(每千个对象 $0.0025)。对于小文件较多的场景,这笔费用可能不划算。
Intelligent-Tiering 的内部实现(基于公开行为推断):S3 为每个启用 Intelligent-Tiering 的对象维护一个”最后访问时间戳”。后台进程定期扫描这些时间戳,将超过阈值的对象标记为移入下一层。移入冷层时,对象的数据可能被重新编码(使用更高比例的纠删码)或迁移到更高密度的存储介质上;当对象被访问时,自动移回频繁访问层,数据重新迁移到热存储介质。
这个设计的工程含义是:Intelligent-Tiering 对每个对象都有状态追踪开销。如果桶中有数十亿个小文件,仅监控费就可能每月达到数千美元——这时候不如根据已知的访问模式直接指定存储类别。Intelligent-Tiering 最适合的场景是”访问模式确实不可预测”的数据集。
六、成本模型分析
6.1 S3 定价的四个维度
S3 的费用不只是”存储费”——理解成本模型需要看四个维度:
- 存储费(Storage)。 按每 GB 每月计费,不同存储类别价格不同。
- 请求费(Requests)。 按请求次数计费,PUT/COPY/POST/LIST 和 GET/SELECT/其他 分别定价。
- 数据传输费(Data Transfer)。 从 S3 传出到互联网按 GB 计费;S3 到同区域 AWS 服务免费或低价。
- 管理与分析费。 包括 Intelligent-Tiering 的监控费、S3 Analytics、S3 Inventory 等。
6.2 各存储类别的成本对比
以 us-east-1 区域为例(价格可能变动,以下为撰文时的参考值):
| 存储类别 | 存储费(\(/GB/月) | PUT 费(\)/千次) | GET 费(\(/千次) | 取回费(\)/GB) | ||
|---|---|---|---|---|
| Standard | 0.023 | 0.005 | 0.0004 | 无 |
| Standard-IA | 0.0125 | 0.01 | 0.001 | 0.01 |
| One Zone-IA | 0.01 | 0.01 | 0.001 | 0.01 |
| Glacier Instant | 0.004 | 0.02 | 0.01 | 0.03 |
| Glacier Flexible | 0.0036 | 0.03 | 0.0004 | 0.01-0.03 |
| Deep Archive | 0.00099 | 0.05 | 0.0004 | 0.02-0.052 |
6.3 成本模型分析示例
下面用一个具体场景来分析存储类别选择:
场景: 1 TB 日志数据,每天写入一次(365 次 PUT/年),前 30 天每天读取 10 次(300 次 GET/年),之后一年内偶尔读取(约 12 次 GET/年)。数据保留 1 年。
# S3 存储类别年度成本估算(简化模型)
storage_gb = 1024 # 1 TB = 1024 GB
puts_per_year = 365
gets_first_30d = 300
gets_rest_year = 12
total_gets = gets_first_30d + gets_rest_year
# Standard
std_storage = storage_gb * 0.023 * 12 # 存储费/年
std_put = (puts_per_year / 1000) * 0.005 # PUT 费/年
std_get = (total_gets / 1000) * 0.0004 # GET 费/年
std_total = std_storage + std_put + std_get
print(f"Standard: 存储 ${std_storage:.2f} + 请求 ${std_put + std_get:.4f} = 总计 ${std_total:.2f}/年")
# Standard-IA
ia_storage = storage_gb * 0.0125 * 12
ia_put = (puts_per_year / 1000) * 0.01
ia_get = (total_gets / 1000) * 0.001
ia_retrieval = storage_gb * total_gets * 0.01 / 1000 # 取回费(简化)
ia_total = ia_storage + ia_put + ia_get + ia_retrieval
print(f"Standard-IA: 存储 ${ia_storage:.2f} + 请求+取回 ${ia_put + ia_get + ia_retrieval:.4f} = 总计 ${ia_total:.2f}/年")
# Glacier Instant Retrieval
gi_storage = storage_gb * 0.004 * 12
gi_put = (puts_per_year / 1000) * 0.02
gi_get = (total_gets / 1000) * 0.01
gi_retrieval = storage_gb * total_gets * 0.03 / 1000
gi_total = gi_storage + gi_put + gi_get + gi_retrieval
print(f"Glacier IR: 存储 ${gi_storage:.2f} + 请求+取回 ${gi_put + gi_get + gi_retrieval:.4f} = 总计 ${gi_total:.2f}/年")这个脚本展示了成本分析的基本思路:存储费按月累计,请求费和取回费按次数和数据量累计。实际分析还需要考虑最小计费对象大小(IA 的 128 KB 最小计费)、最小存储期限的提前删除费用、以及数据传输费。
关键结论:
- 存储量大、访问少 → Glacier Instant 或 Standard-IA 的存储费优势明显
- 存储量小、访问多 → Standard 的低请求费更划算
- 访问模式不确定 → Intelligent-Tiering 省心但有监控费
6.4 常见的成本陷阱
小文件的 IA 陷阱。 Standard-IA 对每个对象按最小 128 KB 计费。如果实际对象只有 1 KB,存储费按 128 KB 算,相当于被放大了 128 倍。大量小文件用 IA 可能比 Standard 更贵。
最小存储期的删除罚款。 Standard-IA 的最小存储期是 30 天,Glacier Instant 是 90 天,Deep Archive 是 180 天。在最小期限内删除或转移对象,仍然按最小期限收费。举个例子:一个对象存入 Glacier Instant 后 10 天就删除了,你仍然要支付 90 天的存储费。
LIST 操作的隐性成本。
ListObjectsV2按请求次数计费($0.005/千次),每次最多返回 1000 个键。列举一个包含 1 亿个对象的桶需要 10 万次请求,费用 $0.50——不算贵,但如果频繁列举就不便宜了。一些应用框架(如 Spark 读取 S3 数据时)会做大量 LIST 操作来发现文件,这个成本容易被忽略。跨区域传输费。 数据从一个区域传输到另一个区域按 GB 计费($0.02/GB),1 TB 的跨区域传输费 $20,可能超过一个月的存储费。
版本控制的存储膨胀。 启用版本控制后,每次覆盖写入都会保留旧版本。如果一个 1 GB 的对象每天更新一次,一个月后桶里有 30 个版本,存储费是 30 GB 而非 1 GB。必须配合生命周期规则清理旧版本(
NoncurrentVersionExpiration)。分段上传的未完成碎片。 分段上传如果中途失败,已上传的分段不会自动清理,会持续产生存储费。建议配置生命周期规则自动清理超期的未完成分段上传:
{
"Rules": [
{
"ID": "abort-incomplete-multipart",
"Status": "Enabled",
"Filter": {},
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
}
]
}七、访问模式与存储类别匹配
7.1 访问模式分类
对象存储中的数据访问模式大致分为四类:
热数据(Hot Data): 频繁读写,延迟敏感。例如网站静态资源、CDN 源站、实时数据管道的中间结果。适用 Standard 类别。
温数据(Warm Data): 偶尔读取,可以容忍稍高的访问成本。例如 30 天以上的日志、非活跃用户的文件、历史报表。适用 Standard-IA 或 Intelligent-Tiering。
冷数据(Cold Data): 很少读取,但需要在分钟级延迟内取回。例如合规归档、季度审计数据、灾备数据。适用 Glacier Instant Retrieval。
冰数据(Frozen Data): 几乎不读取,取回可以等待数小时。例如法规要求保留 7 年的交易记录、历史备份、监控视频归档。适用 Glacier Flexible 或 Deep Archive。
7.2 生命周期策略实战
S3 生命周期规则(Lifecycle Rules)可以自动将对象在存储类别之间转移。下面是一个日志数据的典型生命周期配置:
{
"Rules": [
{
"ID": "log-lifecycle",
"Status": "Enabled",
"Filter": {
"Prefix": "logs/"
},
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 90,
"StorageClass": "GLACIER_IR"
},
{
"Days": 365,
"StorageClass": "DEEP_ARCHIVE"
}
],
"Expiration": {
"Days": 2555
}
}
]
}这个配置的含义:logs/ 前缀下的对象,写入 30
天后转为 Standard-IA,90 天后转为 Glacier Instant
Retrieval,1 年后转为 Deep Archive,7 年后自动删除。
使用 AWS CLI 应用这个配置:
aws s3api put-bucket-lifecycle-configuration \
--bucket my-log-bucket \
--lifecycle-configuration file://lifecycle.json7.3 生命周期策略的注意事项
- 转移方向是单向递进的。
只能从热到冷,不能在生命周期规则中从冷转回热。如果需要把
Glacier 对象恢复为 Standard,要用
RestoreObjectAPI 先取回,再复制到新的 Standard 对象。 - 转移本身有延迟。 生命周期规则的触发不是精确到秒的——AWS 文档说明规则在对象满足条件后的”一天内”执行。
- 最小存储期约束。 从 Standard-IA 转到 Glacier 需要对象在 IA 中至少存储 30 天。如果在第 31 天就触发转移,IA 的 30 天最小存储期已经满足;但如果规则配置为 20 天从 Standard 转到 IA、40 天转到 Glacier,那么 IA 的存储期只有 20 天,仍然按 30 天计费。
7.4 访问模式分析工具
S3 提供了 Storage Lens 和 S3 Analytics(Storage Class Analysis)来帮助分析访问模式:
- S3 Storage Lens: 提供整个组织或账户级别的存储使用和活动趋势。可以看到哪些桶存储量大但访问量低——这些是转为 IA 或 Glacier 的候选。
- S3 Analytics Storage Class Analysis: 针对特定桶或前缀,分析对象的访问频率分布,建议哪些对象应该转移到 IA。分析结果通常需要 30 天以上的观测期才有参考价值。
我认为在实际工程中,最有效的存储类别优化不是靠工具自动分析,而是靠对业务数据生命周期的理解。例如:日志数据的访问模式几乎 100% 可预测——写入后几天内频繁查询、一周后偶尔查询、一个月后几乎不查询。这种确定性的模式直接用生命周期规则比 Intelligent-Tiering 更划算。Intelligent-Tiering 和 Analytics 更适合那些访问模式不确定的数据——例如用户上传的文件,你不知道用户什么时候会再次下载。
八、实战:多场景下的 S3 架构选型
8.1 场景一:数据湖的原始层
数据湖(Data Lake)通常把原始数据(Raw Data)存入 S3 作为”真相之源”(Source of Truth),然后通过 ETL 管道生成加工层数据。
# 使用 aws s3 sync 将本地数据同步到 S3 数据湖
aws s3 sync /data/raw/ s3://my-data-lake/raw/ \
--storage-class STANDARD \
--metadata '{"x-amz-meta-source":"production-db","x-amz-meta-pipeline":"v2"}' \
--exclude "*.tmp"架构要点:
- 原始数据写入后很少修改,但前几天会被 ETL 管道频繁读取 → 使用 Standard 类别
- 30 天后原始数据访问频率下降 → 生命周期规则转为 Standard-IA
- 使用前缀分区(如
raw/year=2025/month=10/day=19/)来利用 S3 的索引分片,提升并发 LIST 和 GET 性能 - 启用版本控制,防止误删除或误覆盖
8.2 场景二:多区域灾备
跨区域复制(Cross-Region Replication,CRR)可以把一个桶的对象自动复制到另一个区域的桶中。这在灾备场景下常用,但需要理解其实现特点:
- CRR 是异步的——源桶写入成功后,复制到目标桶有延迟(通常在 15 分钟内,但不保证)。
- CRR 复制的是对象数据和元数据,但不复制桶策略、生命周期规则等桶级配置。
- CRR 支持复制到不同存储类别——例如源桶用 Standard,目标桶用 Standard-IA,节省灾备成本。
8.3 场景三:合规归档
金融、医疗等行业有法规要求数据保留 N 年。S3 Object Lock 提供了 WORM(Write Once Read Many,一次写入多次读取)功能:
# 创建启用 Object Lock 的桶
aws s3api create-bucket \
--bucket compliance-archive \
--object-lock-enabled-for-object-lock
# 设置默认保留策略:合规模式,保留 7 年
aws s3api put-object-lock-configuration \
--bucket compliance-archive \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "COMPLIANCE",
"Years": 7
}
}
}'合规模式(Compliance Mode)下,任何用户(包括 root 账户)都无法在保留期内删除或修改对象。这和 Glacier Vault Lock 的效果类似,但 Object Lock 可以用在任何存储类别上。
九、性能优化与限制
9.1 S3 的性能基线
S3 在单前缀维度的性能基线(AWS 文档公开数据):
- GET/HEAD: 每秒 5500 次请求
- PUT/COPY/POST/DELETE: 每秒 3500 次请求
注意这是单前缀的限制。如果数据分布在多个前缀下,总吞吐可以线性增长。例如,使用 100 个不同前缀,理论上可以达到每秒 55 万次 GET。
9.2 提升吞吐的关键做法
使用随机化前缀。 如果所有对象都在
data/
一个前缀下,请求会集中到同一组索引分片,成为瓶颈。把键名设计为
data/{hash-prefix}/object-name,利用哈希前缀分散请求到多个分片。
从 2018 年开始,AWS 改进了 S3 的自动分片机制——即使不使用随机前缀,S3 也会根据请求模式自动拆分索引分片。但在突发流量场景下,自动拆分可能来不及。如果能预知高并发需求,提前使用分散的前缀仍然是更可靠的做法。
使用分段上传。 大文件(100 MB 以上)应使用分段上传(Multipart Upload),每个分段独立上传,上传完成后合并。好处是: - 各分段可以并行上传,利用多连接带宽 - 单个分段失败只需重传该分段,不需要重传整个文件 - 分段上传支持暂停和恢复
分段上传的实际操作示例:
# 对 5 GB 文件使用分段上传
# aws s3 cp 默认对大文件自动启用分段上传
aws s3 cp large-dataset.parquet s3://my-bucket/data/large-dataset.parquet \
--expected-size 5368709120
# 手动控制分段大小(每段 100 MB)
aws configure set default.s3.multipart_threshold 100MB
aws configure set default.s3.multipart_chunksize 100MB
# 使用低级 API 手动分段上传(更精细的控制)
# 1. 初始化分段上传
aws s3api create-multipart-upload \
--bucket my-bucket \
--key "data/large-dataset.parquet" \
--query "UploadId" --output text
# 2. 上传各分段(使用返回的 UploadId)
# 3. 完成分段上传使用 S3 Transfer Acceleration。 通过 CloudFront 边缘节点中转数据,利用 AWS 骨干网络加速跨地域传输。适用于客户端距离 S3 桶所在区域较远的场景。
使用 S3 Express One Zone。 2023 年 re:Invent 发布的新存储类别,基于目录桶(Directory Bucket)设计,提供个位毫秒的延迟和每秒数十万次请求的吞吐。它使用单 AZ 部署(不跨 AZ),存储价格高于 Standard 但请求费用大幅降低(PUT 和 GET 费用只有 Standard 的约 50%)。适用于机器学习训练数据、实时分析等对延迟敏感的场景。Express One Zone 的架构和传统 S3 有显著差异——它不使用扁平键名空间,而是引入了真正的目录结构,更接近文件系统的组织方式。
9.3 延迟特征
S3 的延迟特征和块存储完全不同:
| 操作 | 典型延迟 | 影响因素 |
|---|---|---|
| PUT(小对象 < 1 MB) | 50-200 ms | 跨 AZ 写入确认、索引更新 |
| PUT(大对象分段上传) | 取决于带宽和分段并行度 | 网络带宽、分段数 |
| GET(小对象 < 1 MB) | 20-100 ms | 索引查找、数据读取 |
| GET(首字节,大对象) | 20-100 ms | 同小对象 |
| HEAD | 10-50 ms | 只查索引,不读数据 |
| LIST(1000 个键) | 50-200 ms | 索引扫描范围 |
| DELETE | 30-100 ms | 索引标记删除 |
S3 的延迟主要来自网络往返(客户端到 S3 端点)和内部索引查找,而非磁盘 I/O。这也解释了为什么 S3 的延迟优化主要靠减少请求次数(批量操作、预签名 URL、CDN 缓存),而不是像块存储那样优化 I/O 路径。
9.4 请求限流与重试策略
S3 在检测到单个桶或前缀的请求速率过高时,会返回 HTTP 503(Service Unavailable)或 HTTP 429(Too Many Requests)。这不是 Bug,而是保护机制——防止单个租户的突发流量影响其他租户。
应对策略:
- 指数退避重试(Exponential Backoff)。 AWS SDK 默认内置了指数退避重试逻辑。收到 503 时,第一次等 1 秒、第二次等 2 秒、第三次等 4 秒……避免所有客户端同时重试导致的”重试风暴”。
- 抖动(Jitter)。 在指数退避的基础上加随机延迟,进一步分散重试请求。
- 预热桶。 如果预知即将有突发高并发访问,可以提前用渐进式的流量提升来触发 S3 的自动分片扩展,避免突然的流量冲击触发限流。
# AWS SDK 的重试配置示例(boto3)
import boto3
from botocore.config import Config
config = Config(
retries={
'max_attempts': 10,
'mode': 'adaptive' # 自适应重试,根据错误类型调整策略
}
)
s3 = boto3.client('s3', config=config)十、架构设计的取舍与思考
10.1 不可变性的代价
对象存储的”整对象替换”语义是其可扩展性的基础——不需要处理并发写入同一对象的不同部分、不需要维护文件锁、不需要管理块级别的一致性。但这个设计带来了工程上的限制:
- 不支持追加写入(Append)。日志类数据不能持续追加到同一个对象,要么每个时间段生成一个新对象,要么攒够一批再写入。
- 修改一个字节就要重写整个对象。对于 GB
级大对象,修改一个元数据字段也要重新上传整个对象(可以用
CopyObject做服务端复制来修改元数据,不需要下载再上传,但底层仍然涉及数据重写)。 - 不支持原子的重命名。“重命名”实际上是先复制到新键名再删除旧键名,不是原子操作。在复制完成到删除完成之间,两个键名同时存在。
这些限制不是 Bug,而是深思熟虑的设计选择——对象的不可变性让纠删码编码一次即可、让缓存无需考虑一致性失效、让版本管理可以简单地保留所有旧版本。如果需要原地修改、追加写入、文件锁,应该选择块存储(EBS)或文件存储(EFS),而不是试图在对象存储上模拟这些语义。
实际工程中常见的变通方案:
- 追加写入 → 分段写入 + 合并。
每隔一段时间生成一个新对象(如
log-2025-10-19-14-30.gz),后续用 Athena 或 Spark 按前缀查询所有分段。 - 部分修改 → 拆分为小对象。 把一个大文件拆分为多个小对象(如 Parquet 的行组),只重写需要修改的行组。
- 原子重命名 → 用索引表。 在 DynamoDB 或数据库中维护”逻辑名称→S3 键名”的映射,原子更新映射而不移动实际数据。
10.2 扁平命名空间的工程影响
S3
没有真正的”目录”概念。photos/2025/vacation/beach.jpg
中的 / 只是键名的一部分,不是目录分隔符。S3
控制台显示的”文件夹”是通过 ListObjectsV2 的
Prefix 和 Delimiter
参数模拟出来的。
这个设计的工程影响:
- 没有”创建目录”和”删除目录”的概念。删除”目录”实际上是列举并删除该前缀下的所有对象。
- 不存在”目录层级的权限”(IAM 策略可以按前缀控制访问,但这是策略层面的模拟,不是存储层面的目录权限)。
ListObjectsV2对大前缀的列举可能很慢——如果一个”目录”下有数百万个对象,列举需要多次分页请求。
10.3 强一致性后的架构简化
2020 年之后 S3 的强一致性让很多之前需要在应用层处理的一致性问题消失了:
- 不再需要”写入后等待再读取”的重试逻辑
- 不再需要用 DynamoDB 做元数据一致性的外部协调
- 不再需要担心 LIST 操作看不到刚写入的对象
- 数据管道的”写入→通知→读取”模式可以直接使用,不需要额外的一致性检查
这是一个值得注意的架构演进方向:云服务提供商在底层基础设施中承担更多的复杂性,让应用层更简单。但工程师仍然需要理解底层实现——在极端场景下(如每秒数万次写入同一前缀),强一致性的内部协调开销可能导致性能不如预期,这时候需要回到索引分片的层面去理解和优化。
10.4 对象存储的适用边界
我认为选择对象存储时,最重要的判断标准不是”数据量大不大”,而是”访问模式是否符合对象存储的语义”。具体来说:
- 如果数据写入后不需要部分修改 → 适合
- 如果访问模式是”写一次、读多次” → 非常适合
- 如果需要毫秒级延迟 → 不适合,用块存储或缓存
- 如果需要 POSIX 语义(lock、append、seek) → 不适合
- 如果对象数量在百亿级以上但单对象较大 → 非常适合
对象存储不是万能的,但在它适用的场景里,几乎没有替代方案能同时提供这样的持久性、容量和成本效率。
十一、与其他云厂商的对比
S3 定义了云对象存储的事实标准接口,但各家云厂商在实现细节上有差异:
| 维度 | AWS S3 | Google Cloud Storage | Azure Blob Storage |
|---|---|---|---|
| 持久性 | 11 个 9 | 11 个 9 | 11 个 9(LRS: 11 个 9,GRS: 16 个 9) |
| 一致性 | 强一致(2020 年起) | 强一致(发布即支持) | 强一致 |
| 存储类别数 | 7 | 4(Standard / Nearline / Coldline / Archive) | 4(Hot / Cool / Cold / Archive) |
| 最低归档延迟 | 12-48 小时(Deep Archive) | 小时级(Archive) | 小时级(Archive) |
| 最低存储价格 | ~$0.001/GB/月 | ~$0.0012/GB/月 | ~$0.00099/GB/月 |
| S3 兼容 API | 原生 | 提供互操作 XML API | 不兼容(独立 API) |
Google Cloud Storage(GCS)在设计上选择了从一开始就提供强一致性——GCS 没有经历 S3 那样从最终一致到强一致的演进。这并不意味着 GCS 的技术更先进,更可能是因为 GCS 发布时(2010 年)分布式一致性的工程实践已经更成熟,而且 GCS 的初始规模远小于 S3,做强一致性的代价更低。
Azure Blob Storage 在存储类别的命名上和 S3 不同(Hot / Cool / Cold / Archive),但底层逻辑类似。Azure 的一个独特特性是提供了”更改 Blob 层”的 API,可以在不复制数据的情况下改变 Blob 的访问层(类似于 S3 的生命周期规则转移)。
十二、总结
S3 的 11 个 9 持久性和近乎无限的容量,不是靠某一个单一技术实现的,而是多层架构协同的结果:
- 元数据层 管理桶配置和对象位置信息,解耦了控制面和数据面。
- 索引层 通过分片和分布式一致性协议,支撑万亿级对象的点查、列举和版本管理——2020 年的强一致性改造发生在这一层。
- 存储层 通过纠删码取代三副本,用 1.375 倍的存储开销换来比三副本更高的容错能力;跨 AZ 分片分布保障了 AZ 级别的故障容忍;持续数据校验应对静默损坏。
存储类别的差异不只是”价格表上的数字不同”——Standard 和 IA 可能在纠删码配置和存储介质密度上有区别,Glacier 系列则涉及从在线磁盘到近线磁带的介质切换。理解这些实现差异才能做出正确的存储类别选择,而不是单纯比较每 GB 的价格。
成本优化的核心不是”选最便宜的存储类别”,而是”让访问模式和存储类别匹配”——热数据用 Standard,温数据用 IA,冷数据用 Glacier Instant,冰数据用 Deep Archive,不确定的用 Intelligent-Tiering。生命周期规则把这个匹配过程自动化,但前提是你理解每种类别的最小存储期、最小计费大小和取回费用。
参考资料
官方文档
- AWS S3 官方文档:Amazon S3 Documentation
- S3 存储类别说明:Amazon S3 Storage Classes
- S3 定价:Amazon S3 Pricing
- S3 强一致性公告:Amazon S3 Update - Strong Read-After-Write Consistency
re:Invent 演讲
- Andy Warfield, “Diving Deep on S3 Consistency”, AWS re:Invent 2021 (STG301)
- S3 Internals, AWS re:Invent 2023
- Andy Jassy (CEO) Keynote, re:Invent 2024, S3 规模数据(350+ 万亿对象)
论文与书籍
- Brendan Burns 等, “Designing Distributed Systems”, O’Reilly
- Jeff Dean, Luiz Andre Barroso, “The Tail at Scale”, Communications of the ACM, 2013
上一篇:【存储工程】云块存储架构
下一篇:【存储工程】计算存储分离实践
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【存储工程】数据持久性工程
深入分析数据持久性的工程计算——故障率模型、多副本与纠删码的持久性推导、相关故障的影响、实际数据丢失案例与持久性计算器实现
【存储工程】对象存储模型:从文件到对象的范式转变
深入分析对象存储的设计哲学——文件系统与对象存储的本质差异、CAP 权衡、最终一致性到强一致性的演进,以及 S3 API 核心操作实战
【存储工程】S3 API 深度解析
全面剖析 S3 API 的工程细节——Multipart Upload、S3 Select、生命周期策略、跨区域复制、性能优化与 boto3 高级用法
数据库内核实验索引
汇总本站数据库内核与存储引擎实验文章,重点覆盖从零实现 LSM-Tree 及其工程权衡。