1. 先搞清楚一件事:这三个不是“三种硬盘”
很多人一上来就对比 AWS 的 S3、EBS、EFS,第一反应是“不都是存数据的吗,有什么区别”。我曾经见过有同事把数据库备份直接放到 S3 上让数据库进程挂载使用,结果性能惨不忍睹,还以为是 AWS 网络出了问题。其实这三兄弟根本没有可比性,它们服务的抽象层级都不一样。
用生活化的方式理解:EBS 相当于给电脑插一块硬盘,EFS 相当于一台文件服务器,S3 相当于一个无限大的网盘 API。
- EBS(Elastic Block Storage)是块存储,裸设备,只能挂到 EC2 虚拟机上当系统盘或数据盘使用,不能多台机器共享。
- EFS(Elastic File System)是文件存储,天然支持 POSIX 文件系统语义,多个 EC2 实例可以同时挂载同一个文件系统。
- S3(Simple Storage Service)是对象存储,通过 HTTP REST API 读写数据,没有传统文件夹层级的概念,任何地方只要能联网就能访问。
三者的本质差异在于数据如何被组织、如何被访问、如何被共享。搞懂这三个维度,选型就不难了。
这篇文章主要面向三种人:刚接触 AWS 的开发者、准备做架构方案设计的技术负责人、以及工作中需要频繁在存储选型上做决策的运维工程师。我会把三种存储的底层原理、适用场景、成本模型和实际踩坑经验一次性讲清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 底层本质差异:块、文件与对象
2.1 块存储是“裸设备”
块存储是最底层的存储形态,它把数据切分成固定大小的块(block),每个块都有独立的地址。对于操作系统来说,这就是一块可以格式化、分区的原始磁盘。AWS 的 EBS 就是典型的块存储,它不关心文件系统、不关心文件名、不关心目录结构,只负责按地址读写数据块。
正因为这种“不关心”的特性,块存储的性能是最高的,延迟最低,但也决定了它基本只能绑定一台机器使用。文件系统(比如 ext4、xfs、NTFS)是在块设备之上建立的二级抽象,只有格式化了文件系统,操作系统才能以“文件”的形式读取数据。
这就像一块生肉,你要么直接烤(块存储直接跑数据库),要么切碎了加工成肉馅包饺子(格式化成文件系统再用)。EBS 最常见的用途就是 EC2 的系统盘和数据盘,数据库的高 IOPS 需求通常也由 EBS 支撑。
2.2 文件存储是“共享文件夹”
文件存储管理的是层级结构的文件和目录,支持打开、关闭、读写、追加、加锁等操作,遵循标准的 POSIX 语义。EFS 是 AWS 托管的 NFS 文件系统,内部由多台存储节点组成,通过 NFSv4 协议对外提供服务,客户端侧挂载后会显示为一个普通的网络驱动器。
文件存储的核心优势是多主机共享。多个 EC2 实例、on-premises 服务器、容器 Pod 可以同时读写同一个文件系统,数据实时一致。适合内容管理系统共享上传目录、大数据分析集群共享作业数据、应用服务器集群共享配置和日志。
EFS 的数据其实底层也是放在 S3 之上的,AWS 官方文档也承认 EFS 底层使用 S3 作为存储介质,但对外暴露的是标准文件系统接口。所以不要太纠结底层实现,把它当成一个弹性伸缩的 NFS 就好。
2.3 对象存储是“键值存储的巨型版”
对象存储把数据封装成“对象”(Object),每个对象包含数据本身、元数据(Metadata)和一个全局唯一的键(Key)。S3 就是对象存储的标杆实现,访问方式极其简单——HTTP PUT 上传、HTTP GET 下载,支持 HTTPS,因此天然适合互联网场景下的大规模数据分发。
对象存储的容量理论上无限,不需要预置容量,也没有传统的目录层级概念。虽然 S3 控制台里看起来有文件夹路径(比如 s3://bucket/images/logo.png),那只是把 / 作为对象键前缀做展示,本质上还是扁平结构。
对象存储的性能特点是:吞吐量极高,但单次请求延迟相对较高,不适合随机小规模读写的数据库场景。它强调的是“存得住、存得便宜、随便取”,而不是“低延迟、高性能”。
3. S3:对象存储的核心能力与真实成本陷阱
3.1 S3 到底能做什么
S3 是 AWS 上应用场景最广的存储服务,我见过的典型用法大概有以下几类:
静态网站托管。把 HTML、CSS、JS、图片打包传到 S3 bucket,开启静态网站托管后,bucket 就直接变成一个 HTTP 服务器。配合 CloudFront CDN,能撑住很大的访问量,成本比跑 EC2 部署 Nginx 低一个数量级。很多中小项目的官网就是这么挂的。
大数据与分析数据湖。S3 是 Amazon Athena、Redshift Spectrum、EMR 等分析服务的底层数据源。把原始日志、业务数据以 Parquet/JSON 格式落到 S3,然后直接用 SQL 查询,不需要建任何数据库实例,按扫描量付费。
备份与归档。S3 的存储类设计非常细:S3 Standard(标准)、S3 Intelligent-Tiering(智能分层)、S3 Standard-IA(低频访问)、S3 One Zone-IA(单可用区低频)、S3 Glacier Flexible Retrieval(归档)、S3 Glacier Deep Archive(深度归档)。靠生命周期策略,可以把 30 天前的日志自动转成 IA,90 天前转成 Glacier,成本逐级递减。
静态资源存储与 CDN 分发。这可能是最广泛的应用了,图片、视频、App 安装包、用户上传的文件,统统扔 S3,配合 CloudFront 做边缘加速,全球用户访问延迟大幅下降。
3.2 S3 的成本模型:别光看单价
S3 看上去便宜,$0.023/GB/月(标准存储),但这个单价背后藏着几笔容易被忽视的费用。
请求费用。S3 按 PUT/GET/LIST 请求次数单独计费。如果你有一个频繁读写的应用直接对接 S3,请求费用累积下来可能比存储费用还高。我在实际项目里见过一个数据同步脚本,每天几千万次 PUT 请求,月底账单出来后直接被叫去复盘。
取回费用。IA、Glacier 等存储类,读取数据时要额外收取“取回费”(data retrieval fee)。Glacier Deep Archive 取回速度长达 12–48 小时,单价虽然低至 $0.00099/GB/月,但取回时每 GB 要收 $0.02 左右,量大的时候这笔费用非常可观。
流量费用。数据从 S3 传到互联网,每 GB 收 $0.09;但是从 EC2 传到同区域的 S3,免费。所以架构设计时,能走内网的流量绝不走公网。
成本优化的核心策略就是:生命周期策略必须配置,访问频次低的数据必须转移存储类,大文件读取必须走 CloudFront 而不是直连 S3。
3.3 S3 的一致性模型与常见误区
S3 现在提供强一致性读写,2015 年之前是最终一致。但我发现很多人对 S3 的强一致性理解有偏差——所谓强一致是指“对象数据读后写一致”,也就是你写入一个 key 后立刻 GET,能读到最新值。这不代表 S3 能做数据库用。
S3 的性能特征是单桶可以达到 3500 个 PUT/COPY/POST/DELETE 请求每秒,GET 请求每秒 5500 个。听着不少,但如果你把数据库这种高并发随机小 IO 的场景直接放到 S3 上,惨不忍睹。
还有一点,S3 的“文件夹”删除操作特别容易踩坑。控制台里删除一个“文件夹”,实际上是批量删除所有前缀匹配的对象,如果文件夹下有海量对象,这个操作极其耗时,而且产生的 DELETE 请求都是按量计费的。删了几万个对象,费用虽然不多,但请求量大了也是白花冤枉钱。
4. EBS:性能爆炸但绑定主机的虚拟硬盘
4.1 EBS 的类型选择与性能指标
EBS 目前主流的是 gp3 和 io2 两种。gp3 是通用型 SSD,基础性能 3000 IOPS、125 MB/s 吞吐,用户可以额外付费提升 IOPS 和吞吐量。io2 是极致 IOPS 型,单卷最高可达 64 万 IOPS,适合 Oracle、SAP 这类重数据库。
选择逻辑其实不复杂:
- 系统盘:gp3 起步,除非你有极端的启动性能要求。
- 数据盘(数据库/消息队列):gp3 加大 IOPS,或者直接 io2。
- 吞吐密集型(视频处理、大数据分析):gp3 调高吞吐量参数,或者选 HDD 类型的 st1/sc1。
需要注意的一个关键点:EBS 的性能参数是按卷(Volume)粒度计算的,不是共享的。你创建了一个 1000 IOPS 的卷,只有挂载这个卷的 EC2 实例能用到这些 IOPS,其他实例完全没份。
4.2 EBS 与 EC2 的绑定关系与生命周期
EBS 卷创建后属于某个可用区(Availability Zone),只能挂载到同一个可用区的 EC2 实例。跨可用区迁移数据,必须先创建快照(Snapshot),再从快照恢复到目标可用区,这个操作时间取决于数据量。
EBS 卷和 EC2 实例的删除策略也要特别注意。默认情况下,EC2 控制台创建实例时的系统盘勾选了“Delete on termination”,数据盘默认不勾选。如果创建时没注意,实例终止后系统盘连带数据一起被销毁,且不可恢复。我有个同事在生产环境遇到过这种事故,恢复靠快照花了十几个小时。
EBS 的快照是增量快照,第一次全量备份,后续只备份变化的数据块。所以定期做快照的成本比很多人想象的低得多,但要小心大量快照的存储费用累积。实战中建议至少保留最近 3–7 天的快照,更早的快照可以复制到 S3 Glacier 做长期存档。
4.3 EBS 的扩展与性能优化实操
EBS 卷扩容非常简单:修改卷大小,等待状态变为“优化中”再到“完成”,然后登录实例执行 growpart 和 resize2fs 扩展文件系统。这个过程在 Linux 上顺序不能反,先扩分区再扩文件系统,否则系统识别不到新空间。
IOPS 不够用怎么办?优先确认实例本身的网络带宽和 EBS 带宽限制,因为 EC2 实例类型不同,对 EBS 的吞吐上限也不同。一个 t3.micro 即使是 io2 卷,也跑不满高 IOPS。
启动性能优化:开启 EBS 优化(EBS-optimized)是默认行为,大部分现代实例类型都默认支持。数据库场景建议开启多重挂载(Multi-Attach)的 io2 卷,可以让多个 EC2 同时挂载同一个卷,适合做 Oracle RAC 这一类集群数据库场景,但要注意文件系统必须支持集群并发访问,ext4 是不行的,需要用集群文件系统。
5. EFS:按需伸缩的共享文件系统
5.1 EFS 的工作原理与挂载方式
EFS 本质上是一个托管的 NFS 文件系统,创建后先用安全组控制允许访问的客户端,然后在 EC2 上执行 mount -t nfs4 -o nfsvers=4.1 挂载。挂载命令里有个重要的参数是 -o tls,可以启用传输加密,建议生产环境必开。
EFS 的最大卖点是不需要预置容量,文件系统自动扩展,你只有实际存储的数据量按 GB 计费,不用关心分区、扩容、RAID 等琐事。对于文件数量、数据量都不可预测的场景,这一点特别友好。
EFS 的性能模式有两种:通用模式(General Purpose) 适合延迟敏感的场景,比如 Web 服务共享目录、代码库;最大 I/O 模式(Max I/O) 适合高吞吐场景,比如视频渲染、大数据分析。创建后性能模式不可切换,所以选之前必须想清楚。
5.2 EFS 的三种吞吐模式
EFS 的吞吐量模式设计得比较有意思,分为突发模式(Bursting)、弹性模式(Elastic)和预置模式(Provisioned)三种。
突发模式下,吞吐量基于文件系统的存储量按比例提供基准吞吐,存储量越大,基准吞吐越高。数据量小但有突发访问时,可以“借用”吞吐积分(类似 CPU burst credits),短时间内跑很高的吞吐,但积分用完就不行了。
弹性模式是最省心的,吞吐量可以自动扩展到非常高的水平,适合吞吐模型不太确定的负载。我当时第一次看 Elastic 模式账单时也愣了一下,因为它是按实际使用的吞吐量计费的。
预置模式适合有稳定高吞吐需求的场景。比如固定跑 500 MiB/s 的写入,直接用预置模式把吞吐量设置到 500,不会浪费钱,也不用担心突发积分不够。
实践建议:如果你的数据量很大且吞吐需求稳定,预置模式最划算;如果是一般 Web 服务场景,突发模式够用且省钱;如果有临时性的大吞吐需求,弹性模式最省心。
5.3 EFS 的经典使用场景与局限性
我实际用过 EFS 的几个场景:
Web 集群共享上传目录。多个 Web 实例挂在同一个 EFS 上,用户上传的图片写入 EFS,所有实例都能读到,不需要再单独做文件同步。配合生命周期策略,把 30 天不访问的文件自动转为 EFS IA 存储类,成本进一步压缩。
CI/CD 共享工作目录。Jenkins/GitLab Runner 构建时,多个构建机共享一个 EFS,可以缓存 Maven/Gradle/npm 依赖。大规模构建时能明显缩短构建时间,避免每一台机器都缓存一份依赖包。
大数据分析临时文件交换。EMR 集群多个节点作业时,中间结果放到 EFS,下一阶段直接读取,省去每台机器单独复制文件的麻烦。
但 EFS 有明显的短板:延迟比 EBS 高一个数量级。虽然通用模式延迟已经优化到个位数毫秒级别,但和本地挂载的 EBS 相比还是差不少,不适合数据库数据文件或者高随机 IO 的场景。没有特别说明的场景,不要把数据库放在 EFS 上。
6. 三种存储的核心差异对比
很多人在选型时最纠结的就是“哪个好”这个问题。我的建议是:没有绝对的好坏,只有合不合适。与其纠结,不如先把三者的差异用一张表直接对比清楚。
| 对比维度 | S3(对象存储) | EBS(块存储) | EFS(文件存储) |
|---|---|---|---|
| 数据抽象层级 | 对象(Object) | 数据块(Block) | 文件和目录(POSIX) |
| 访问方式 | HTTP REST API | 挂载为本地磁盘 | NFS 挂载 |
| 共享能力 | 多客户端随意访问 | 默认单实例挂载 | 多实例并发访问 |
| 容量扩展 | 无限自动扩展 | 单卷上限 64TiB | 自动按需扩展 |
| 延迟水平 | 毫秒级(较高) | 亚毫秒级(极低) | 毫秒级(中等) |
| 持久性 | 11 个 9(99.999999999%) | 同可用区 99.999% | 多可用区冗余 |
| 性能场景 | 大吞吐、批量读写 | 随机小 IO 低延迟 | 共享并发读写 |
| 典型用途 | 备份、静态资源、数据湖 | 系统盘、数据库盘 | Web 共享目录、大数据作业 |
| 成本特征 | 存储+请求+流量分别计费 | 按预置容量计费,IOPS 可加购 | 按实际使用容量+吞吐模式计费 |
三者的最大不同在于共享能力和延迟性能的取舍。EBS 性能最好但是独占;EFS 能共享但延迟稍高;S3 什么都能放但是不能当文件系统挂载(虽然有 Mountpoint for S3 可以把 S3 挂载成文件系统,但用途有限,别被忽悠了)。
6.1 为什么不能把数据库放在 S3 上
这个问题我回答过无数遍。数据库的常见工作负载是随机小 IO,比如更新一行记录,可能涉及读取、修改、写回多个页,每个页的大小是 8KB 或 16KB。S3 每次读写至少要发起一个 HTTP 请求,哪怕你只修改 1 个字节,也要完整走一遍 PUT 或 GET 的流程,延迟和开销完全不可接受。
EBS 则不同,EC2 实例和 EBS 卷之间走的是专用网络,数据路径极短,延迟低至亚毫秒级。数据库跑在 EBS 上,IO 路径和本地硬盘几乎无差别。
S3 适合的是“一次性写入、多次读取、数据量大、数据不常变”的对象型数据。两者的生理结构决定了它们的天职不同。
6.2 什么时候选 EFS 而不是 EBS
当你的业务需要多台机器共享同一份文件数据时,EFS 是更合理的选择。比如有 5 台 Web 服务器要读取同一个上传目录,如果用 EBS,你得做文件同步或者分布式文件系统,复杂度上升一个量级。EFS 天然支持这个场景,而且容量自动扩展,不用担心单卷容量不够。
反过来,如果是单机性能敏感型应用,比如一个独立的 MySQL,那 EBS 是唯一正解。EFS 的 NFS 访问链路经过网络协议栈和文件系统映射,延迟和吞吐都比不上本地 EBS。
一句话总结选型逻辑:
- 代码/数据需要被一台机器高性能访问 → EBS
- 文件需要被多台机器共享访问 → EFS
- 任何数据需要被 API 访问且量大不常变 → S3
7. 实际项目中的选型决策路径
7.1 从业务场景反推存储选型
我在多个项目里的存储选型方法基本是“先列业务需求,再对号入座”。核心要回答以下问题:
- 数据量级是多少?几百 GB 还是几百 TB?
- 访问模式是什么?随机小 IO,顺序大吞吐,还是低频读取?
- 数据一致性要求多高?能不能接受最终一致?
- 需要被多少台机器共享访问?
- 数据生命周期和访问频次变化大吗?
- 预算约束是多少?
把这几个问题回答了,选型结果基本就出来了。比如一个典型的企业应用:关系型数据库用 RDS + EBS 底层托管,用户上传的头像和附件存 S3 + CloudFront,多个应用节点共享的临时上传目录用 EFS。这样的组合基本能覆盖 90% 以上的常规业务场景。
7.2 一份可参考的多层存储组合方案
这里给一个我们实际用过的组合方案,供大家参考:
第一层(热数据层):RDS / EC2 数据库,底层 EBS gp3,IOPS 按需调高,存放交易记录、用户表等核心业务数据。
第二层(共享层):应用服务器集群共享的临时文件、上传目录,用 EFS。生命周期策略 7 天后自动转 EFS IA。
第三层(对象层):最终落库的静态资源、备份、日志、导出文件,统一进 S3 bucket。S3 桶内生命周期策略:30 天转 IA,90 天转 Glacier,180 天转 Deep Archive。
第四层(加速层):S3 前面接 CloudFront,全球用户访问静态资源的延迟降到几十毫秒以内。
这套方案的优点是每一层都有清晰的职责边界,数据从热到冷自动流转,运维成本和人工干预都很少。缺点是方案复杂度较高,需要处理 S3 事件通知、生命周期配置等细节,但投入产出比非常划算。
7.3 AWS CLI 实操指令:三分钟创建三类存储
如果你刚开始测试或者在自建环境里复现,下面几个 AWS CLI 命令是基础中的基础:
创建 S3 bucket:
bash复制aws s3api create-bucket --bucket my-demo-bucket-2024 --region ap-northeast-1 --create-bucket-configuration LocationConstraint=ap-northeast-1
上传一个文件到 S3:
bash复制aws s3 cp ./local-file.zip s3://my-demo-bucket-2024/backups/
创建 EBS 卷并挂载到 EC2:
bash复制aws ec2 create-volume --availability-zone ap-northeast-1a --size 100 --volume-type gp3
aws ec2 attach-volume --volume-id vol-xxxxx --instance-id i-xxxxx --device /dev/sdf
登录实例后格式化并挂载:
bash复制sudo mkfs.xfs /dev/nvme1n1
sudo mkdir /data
sudo mount /dev/nvme1n1 /data
但要注意,EBS 不是创建好直接用就完了,很多新手栽在文件系统格式化和禁用 UUID 冲突上。复制 EC2 后如果系统盘的 UUID 和新挂载的数据盘重复,开机时会挂载失败。建议通过 /dev/disk/by-id 或者直接改 /etc/fstab 里的挂载配置来解决。
创建 EFS 文件系统:
bash复制aws efs create-file-system --creation-token my-efs-demo --performance-mode generalPurpose
获取挂载地址后,在 EC2 上挂载:
bash复制sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 fs-xxxx.efs.ap-northeast-1.amazonaws.com:/ /mnt/efs
挂载参数里我最看重的是 timeo=600 和 retrans=2,这两个参数控制了 NFS 超时和重传机制,如果不调整,网络抖动可能导致应用卡死。
8. 日常运维中常见的坑与排查思路
8.1 S3 数据“删了就没了”与版本控制
S3 的一个经典事故场景:误删了生产环境的某个 prefix,然后整个团队开始焦虑。如果 bucket 没有开启版本控制,删除操作是不可逆的。AWS 官方的建议是:
所有重要 bucket 必须开启版本控制(Versioning),并且配置生命周期规则定期清理旧版本。这不仅能防止误删,还能在数据被恶意覆盖时快速恢复。
另外,S3 的删除操作分为删除对象和删除 bucket 两种。删除 bucket 前必须清空所有对象和版本,命令行工具 aws s3 rb s3://bucket --force 可以直接连桶带对象一起删,但生产环境慎用。
8.2 EBS 卸载时的数学题:5 分钟丢失数据
EBS 在挂载状态下做快照时,虽然可以对热卷打快照,但一致性只保证崩溃一致性,数据库这类在内存有缓存的应用,仅靠 EBS 快照无法保证能恢复到一致状态。正确做法是:先停应用、卸载卷(或使用数据库的一致性备份工具)、再打快照。
我以前处理过一个线上数据库事故,就是因为依赖 EBS 快照做恢复,结果快照拿回来后数据库文件处于中间状态,启动后报错 InnoDB 损坏。从那以后,我们的所有生产数据库备份都是先通过 mysqldump 或 RDS 自带备份机制来做,EBS 快照只作为辅助手段。
8.3 EFS 挂载后性能差
EFS 挂载后访问性能差的常见原因有三个:
第一,安全组没配好,EC2 和 EFS 之间的网络路径绕了远端。
第二,未开启 -o tls 参数,部分客户端挂载性能差距很大。
第三,大量小文件操作。EFS 对大量小文件的随机读写性能很差,因为每次文件操作都要经过 NFS 协议传输,元数据操作开销占大头。如果是大量小文件的场景,要么用 S3,要么用 EBS 本地处理后再上传。
我测试过一个行为:EFS 上用 find 命令遍历一个包含 100 万个小文件的目录,花了将近 40 分钟;同样的操作在 EBS 本地盘上跑,几分钟就完成了。所以小文件密集场景慎用 EFS。
8.4 成本爆炸的三种典型姿势
再多说几句成本方面的经验。
第一,S3 没有配置生命周期策略,数据全部躺在 Standard 存储类上吃灰。实际上 90% 以上的历史文件在三个月后都没人访问了,放着 Standard 就是烧钱。
第二,EFS 没有启用生命周期管理,老数据全部留在标准存储层。EFS 的 IA 存储类比标准存储便宜约 60%,自动转存储类的配置不复杂,几分钟就能完成。
第三,EBS 快照做了之后不清理。快照是增量备份,但增量快照链会越积越大,旧快照如果不删,费用一样堆得老高。建议设置定期清理策略,只保留最近 7 天的日级快照和最近 3 个月的月级快照。
9. 最后分享一个我踩过三次的坑
这个话题快讲完了,最后再说一个我反复踩过三次的坑,希望能帮大家省点学费。
EC2 实例存储(Instance Store)和 EBS 的混淆。AWS 上“实例存储”和“EBS 存储”是完全不同的东西。实例存储是 EC2 宿主机的本地磁盘,性能极高但不是持久化存储,实例停止或终止时数据会全部丢失。EBS 是网络存储,独立于实例生命周期存在。
有一次我们把 Kafka 的日志目录放在了实例存储盘上,因为没有做好副本备份,某次宿主机维护重启后,整个 Kafka 的数据直接消失得干干净净。事后排查确认,实例存储盘根本不在持久化范围内。
所以,任何有持久化需求的数据都必须放 EBS、EFS 或 S3,实例存储只能用于临时数据、缓存、sort buffer 之类可以随时重算的内容。如果你的业务数据存在实例存储盘上,现在就迁移,不要等出事了再后悔。
最后说一个我个人的习惯:所有存储配置完成后,我都会做一次“断电演练”——把实例停止、把 bucket 权限改成拒绝访问、把 EFS 挂载点卸载,确认在生产环境真正遇到突发事件时,数据还能完好无损地拿回来。存储这行,永远给自己留一条后路。
