本篇要回答的问题:为什么单机文件系统不适合存储互联网时代的海量非结构化数据?对象存储凭什么取而代之,它与文件系统的本质区别是什么?以及成本与持久性之间该如何权衡?
上一组讲座建立了"存储中间件"的认知(业务状态、数据库的由来)。本讲从"存储为何复杂"切入,沿着单机文件系统 → 分布式文件系统(GFS/HDFS)→ 对象存储这条演进路线,讲清楚服务端存储与桌面操作系统从此分道扬镳的关键节点。
异常处理才是存储的"业务逻辑"
存储 = 维持计算系统状态的单元,与生俱来就背负可靠性要求。普通业务程序只需关注正常分支、出错抛异常即可;存储系统恰恰相反——庞杂多样的异常分支处理才是它的"正常业务逻辑"。这正是数据库这类存储中间件必然出现的根本原因。
| 视角 | 普通业务程序 | 存储系统 |
|---|---|---|
| 精力重心 | 正常分支流程 | 各类异常情况处理 |
| 出错策略 | 抛错告诉用户"别这么玩" | 必须正确兜底、把损失降到最低 |
| 核心指标演进 | —— | 单机时代→可靠性;C/S、B/S 时代→新增可用性 |
关键判断:没有存储中间件,每个业务都要自己持久化状态,负担沉重;有了高可用存储中间件,业务程序只管操作它来更新状态,靠多实例互备即可实现"逻辑上不宕机"。
单机文件系统的四大硬伤
业务里的多媒体(图片、音视频、Office 文档)一般放文件系统而非数据库。但单机时代诞生的文件系统并不适合存这些数据:
| 问题 | 表现 |
|---|---|
| 伸缩性 | 单机容量有限,超过一台机器就没辙 |
| 性能瓶颈 | 文件数到临界点后性能快速下降,大容量盘很容易触达 |
| 持久性(Durability) | 通常单副本;需冗余(经典做法 3 副本)才能扛住坏盘 |
| 可用性 | 单副本下机器宕机即不可读不可写 |
关键判断:服务端存储只要规模够大,很多看起来的小概率事件就会变成必然事件。RAID5 单机约"100 年丢一次"(2 个 9),但 100 台就变成 1 年一次,修复时间拉长到 3 天甚至 15 天一次——这个数字无法接受。
GFS / HDFS 与海量小文件的错配
GFS 论文奠定了 3 副本地位,Hadoop 据此实现了开源版 HDFS。但 HDFS 适合日志存储与分析,不适合海量富媒体小文件:
| HDFS 特性 | 为何不适合海量小文件 |
|---|---|
| block 大小 64M | 不足 64M 也占 64M;图片常规仅几百 K,浪费严重(调小 block 是错误做法) |
| 单 Master 结构 | 元数据条目数有限,小文件场景很快触达伸缩性瓶颈 |
| 沿用文件系统 API(有目录) | 分布式维护目录树极难,Master 难以扩展为分布式元数据集群 |
对象存储:去关系、去目录
非结构化数据(图片、音视频、文档)占互联网传输量 90% 以上,它以"用户体验友好"而非"机器友好"组织,最佳存储方式是键值存储——用于非结构化数据的 KV 存储有个专名叫对象存储(Object Storage)。
| 维度 | 文件系统 | 对象存储 |
|---|---|---|
| 核心抽象 | 目录树(有父子关系) | 扁平的 Key → Value |
| Key 中的 “/” | 路径分隔符,有目录语义 | 只是一个普通字符,无目录概念 |
| 分布式友好性 | 维护目录树很难 | 对 Key 做 Hash / Range 分区即可定位到单机 |
| 与 NoSQL 的共性 | —— | NoSQL 本质是**“去关系”**(去多索引)而非"去 SQL" |
关键洞见:对象存储的出现,是服务端体系架构与桌面操作系统分道扬镳的起点。文件系统只是桌面 OS 为方便用户手工管理数据而设计的产物,对服务端来说是过时的东西。
第一个公认的对象存储是 AWS S3,最基本接口仅两个:
func PutObject(bucket, key string, object io.Reader) (err error)
func GetObject(bucket, key string) (object io.ReadCloser, err error)
而七牛云存储不只是分布式存储,还额外解决了网络与处理问题:
七牛云存储 = 对象存储 + 上传下载加速(弱网/大文件上传、CDN 下载)+ 多媒体处理(缩略图、音视频转码)
成本与持久性的定性权衡
对象存储占 90% 以上存储需求,最敏感的就是单位存储成本(元 / GB / 月)。成本的两大关联因素:
| 因素 | 含义 | 降成本手段 |
|---|---|---|
| 存储密度 | 单台机器存储量(盘数 × 单盘容量) | 提高密度 |
| 冗余度 | 数据复制倍数 | 用纠删码(EC) 替代 3 副本 |
EC 示例(28 + 4):文件切 28 份 + 算 4 份冗余 = 32 份存 32 台机器。
| 指标 | 3 副本 | EC 28+4 |
|---|---|---|
| 成本(存 1PB) | 100% | 约 38%(32/28/3) |
| 容错(可同时坏) | 2 块盘 / 2 台机 | 4 块盘 / 4 台机 |
关键判断:冗余度降低不必然伤害持久性与可用性——它们与冗余度不是正相关,而是与集群的容错能力相关。
持久性(Durability)取决于两个关键参数:单位修复时长 T0 与 容错能力 M(N+M 方案可同时坏 M 块)。直觉是:T0 时间内同时坏 M 块盘的概率 = 丢数据的概率。
| 变量变化 | 对持久性的影响 | 原因 |
|---|---|---|
| 单机磁盘数 ×2(密度不变) | 几乎无影响 | T0 不变,丢失概率仍约 p(但可用性可能下降) |
| 单盘容量 ×2(提高密度) | 有较大伤害,约 2p | 修复量↑、可用盘数↓ → T0 变 4T0;盘数减半又部分抵消 |
| 集群规模 ×2(扩容) | 整体正向 | 修复变快(T0→0.5T0)vs 坏盘概率↑,一正一反,严谨演算偏正 |
关键洞见:在网络与算力不成瓶颈的前提下,集群规模越大,存储可靠性越高(前提是修复速度与集群规模成正比)。
总结
本讲沿着存储演进史,说清了"为什么是对象存储":单机文件系统在伸缩性、性能、持久性、可用性上全面失守;HDFS 是为大文件日志而生,并不适合海量小文件;真正承接非结构化数据洪流的是去目录、去关系的对象存储。最后用定性分析厘清了成本(存储密度、冗余度)与持久性(T0、M)的微妙关系。
先把"是什么"回答清楚
| 概念 | 一句话说明 |
|---|---|
| 存储中间件 | 替业务统一承担状态持久化,使业务可"逻辑上不宕机" |
| 持久性(Durability) | 数据不丢的能力,由单位修复时长 T0 与容错能力 M 决定 |
| 非结构化数据 | 以用户体验而非机器友好组织的数据(多媒体),占传输量 90%+ |
| 对象存储 | 存非结构化数据的 KV 存储,Key 像路径但无目录概念 |
| 纠删码(EC) | N+M 算术冗余,以远低于 3 副本的成本提供更强容错 |
一句话速记
文件系统是桌面 OS 给人用的目录树,对象存储是服务端给机器用的扁平 KV——用纠删码而非多副本,用 Hash/Range 分区而非目录树,是服务端存储与桌面 OS 分家的标志。
几条值得记住的判断
- 存储的"正常业务逻辑"就是异常处理;这是它复杂的根源。
- 规模一大,小概率事件变必然事件——架构必须按"必然会坏"来设计。
- 降冗余度不必然降持久性;真正决定持久性的是容错能力 M 与修复时长 T0。
思考题
对象存储用"去目录、去关系"换来了分布式可扩展性。回到你自己的系统:你是否还在用"目录树 / 多索引"这类便于人理解、却难以水平扩展的抽象,去硬扛本应交给扁平 KV 的海量数据?
