文件存储系统设计 (S3/网盘)
一、需求分析
设计一个类似 Amazon S3 的对象存储服务,支持:
- 海量存储:存储 350 万亿对象、EB 级数据
- 高持久性:11 个 9(99.999999999%)的持久性保证
- 高可用性:99.99% 以上的可用性
- 低延迟:读写延迟控制在毫秒到百毫秒级
- 强一致性:写入后立即可读
二、核心数据模型
对象存储与文件系统有本质区别。对象存储是扁平的键值模型(Bucket + Key → Object),而非层次化的目录树。
| 概念 | 说明 |
|---|---|
| Bucket | 命名空间容器,全局唯一名称 |
| Object Key | 对象在 Bucket 内的唯一标识,如 photos/2024/vacation.jpg |
| Object Data | 对象本身的二进制数据 |
| Metadata | 键值对形式的元数据,如 Content-Type、自定义标签 |
| Version ID | 启用版本控制后,每次更新生成新版本号 |
三、系统架构
1 | 用户请求 |
3.1 元数据服务
对象元数据(key、大小、ETag、存储位置)存储在分布式数据库中。元数据服务的核心挑战:单表可能达到千亿行级别,需要精心设计分片策略。
分片方案:按 Bucket + Key 前缀水平分片。
1 | Shard-0: bucket-a/photos/ → bucket-a/videos/ |
查询优化:
GetObject(key)→ 单行查询,直接走主键索引ListObjects(prefix)→ 前缀扫描,利用键的字典序排列
3.2 数据存储服务
对象数据存储在分布式文件系统或裸盘上。每个对象被分割为固定大小的 chunk(如 64MB),分散存储在不同节点上。
关键设计决策:
- 不需要目录树结构 → 简化了一致性模型
- 每个 chunk 有多个副本(通常是 3 副本,分布在不同机架/机房)
- 小对象(< 1MB)可以合并在同一个 chunk 中以减少元数据开销
四、持久性与容错
4.1 11个9的持久性如何保证
| 层次 | 措施 | 目标 |
|---|---|---|
| 副本 | 3 副本写入(跨机架、跨数据中心) | 单盘/单机故障不影响数据 |
| 校验 | 每 chunk 附带 CRC32/MD5 校验和,读取时验证 | 检测静默数据损坏 |
| 自愈 | 后台进程定期扫描副本完整性,发现缺失自动补充 | 自动化运维 |
| 版本控制 | 启用后保留所有历史版本,防止误删/误改 | 应用层容错 |
| 跨区域复制 | 异步复制到另一个区域(如 S3 CRR) | 灾难恢复 |
4.2 纠删码 vs 多副本
| 方案 | 存储开销 | 容错能力 | 计算开销 |
|---|---|---|---|
| 3 副本 | 3x | 同时丢失 2 节点 | 无 |
| Reed-Solomon(6,3) | 1.5x | 任意 3 个块丢失可恢复 | 编解码开销 |
大对象更适合用纠删码(节省存储成本),小对象用多副本(避免编解码延迟)。
4.3 写入一致性
为保证”写入后立即可读”,对象存储通常采用 Quorum 机制:
- 写入:需要 W 个节点确认后才返回成功
- 读取:需要 R 个节点确认后返回数据
- 当 W + R > N 时,保证强一致性
五、大文件分块上传
5.1 为什么需要分块
- 可靠性:上传 10GB 文件,中途失败不需要重传全部
- 并行性:多个分块可以并行上传到不同存储节点
- 断点续传:记录已上传的分块,恢复时只需上传剩余部分
5.2 分块上传流程
1 | 1. Initiate Multipart Upload → 返回 Upload ID |
服务端只需要记录 (Upload ID, Part Number, ETag) 的映射关系,合并时按 Part Number 排序拼接即可,不需要实际的物理合并。
六、访问控制
对象存储的访问控制通常支持三层:
| 层次 | 机制 | 示例 |
|---|---|---|
| IAM 策略 | 绑定到用户/角色的权限策略 | “允许对 Bucket-A 的读操作” |
| Bucket 策略 | 绑定到 Bucket 的访问规则 | “允许来自特定 VPC 的请求” |
| 预签名 URL | 临时授权的下载/上传链接 | 生成有效期 1 小时的下载链接 |
预签名 URL 的原理:
1 | URL = https://bucket.s3.com/object?Signature={HMAC-SHA256(SecretKey, StringToSign)}&Expires={timestamp} |
七、容量估算
假设 1000 万个用户,人均存储 50GB:
- 总存储需求:1000万 × 50GB = 500PB
- 3 副本开销:500PB × 3 = 1.5EB
元数据估算:每个对象 64 字节元数据,千亿对象 → 6.4TB 元数据(可全部放在内存中)。
八、小结
对象存储的核心设计原则是扁平命名空间 + 分块存储 + 多副本容错。相比传统的 POSIX 文件系统,它牺牲了目录层级和原地修改的能力,换来了近乎无限的扩展性和极高的持久性。Amazon S3、Google Cloud Storage、阿里云 OSS 等主流对象存储服务都遵循这一架构范式。