块、文件、对象:三种存储语义
不是三种产品,是三种访问语义。选错语义,后面怎么调优都别扭。
学完这节你能做到
- 用一句话说清三种存储各自暴露给应用的是什么
- 给定业务场景,判断应该用块、文件还是对象
- 解释为什么对象存储天然易扩展而文件存储难
三种存储,其实是三种语义
刚入行时很容易把块、文件、对象理解成"三种产品",于是产生一个疑问:Ceph 为什么能同时是三种?
答案是:它们不是三种产品,而是三种暴露给应用的访问语义。底层都是"把字节存到很多块盘上",区别在于对外提供什么接口、承诺什么语义。Ceph 底层只有一个 RADOS 对象存储池,上面套三层不同的翻译器,就变出了三种存储。
| 块存储 | 文件存储 | 对象存储 | |
|---|---|---|---|
| 应用看到的 | 一块裸磁盘 | 目录树 | 一个扁平的桶 |
| 访问单位 | 扇区 / LBA 偏移 | 文件 + 偏移 | 整个对象 |
| 典型操作 | 读写某个偏移 | open/read/write/seek/rename | PUT / GET / DELETE |
| 能否多机同时写 | 一般不能(除非集群文件系统) | 能 | 能 |
| 修改一个字节 | 可以 | 可以 | 通常要重写整个对象 |
| 协议 | iSCSI、NVMe-oF、RBD | NFS、SMB、CephFS、GPFS | S3、Swift |
| 扩展性 | 中 | 较难 | 极好 |
块存储:语义最少,所以最快
块存储提供的东西极其原始:一个按扇区编址的线性地址空间。它不知道什么是文件,也不知道什么是目录——那是上层文件系统的事。
# 拿到一个 RBD 块设备,它和本地盘用起来没有区别
rbd create mypool/disk01 --size 100G
rbd map mypool/disk01 # → /dev/rbd0
mkfs.xfs /dev/rbd0 # 需要自己建文件系统
mount /dev/rbd0 /mnt/data
正因为语义少,块存储的路径最短、延迟最低。它的典型用户是:
- 虚拟机的系统盘和数据盘
- 数据库(MySQL、PostgreSQL)—— 它们自己管理数据布局,不需要文件系统帮忙
- 需要独占一块高性能空间的应用
块存储默认是单挂载语义。两台机器同时把同一个 RBD 挂载成 XFS 并写入,会因为两边各自缓存元数据而直接把文件系统写坏,且没有任何报错提示——直到某天你发现目录结构乱了。要多机共享,必须用共享文件系统(CephFS/GPFS),或者用支持共享的集群文件系统(GFS2/OCFS2)。K8s 里对应的就是 ReadWriteOnce 与 ReadWriteMany 的区别。
文件存储:好用,但元数据是代价
文件存储提供目录树和 POSIX 语义:能 seek,能改名,能设权限,能多客户端同时读写同一个目录。开发最喜欢它,因为不用改代码。
代价在元数据。每次 ls 一个目录、每次 open 一个文件、每次 stat,都要访问元数据服务。数据量可以靠加盘线性扩展,但元数据操作的扩展要难得多:
- CephFS 靠 MDS 承载元数据,MDS 把热元数据放在内存里,内存不够时性能会锐减
- GPFS 把元数据分布到所有节点,扩展性更好,但换来了更复杂的运维
- NFS 的每一次元数据操作都是一次网络往返,小文件场景下网络往返次数会成为主要开销
读 100 万个 4KB 小文件,和读 100 个 40GB 大文件,数据总量可能相近,但前者要多做 100 万次元数据操作。AI 训练场景里数据集常常是海量小图片,这就是为什么 storplan 里明确写着 CephFS 不建议用于 AI 场景——它的元数据缓存受单个 MDS 的内存限制,撑不住这种压力。
对象存储:牺牲语义,换来无限扩展
对象存储把目录树扔掉了。它只提供一个扁平的键值空间:桶(bucket)里放对象(object),键就是名字。
# S3 语义:整存整取
aws s3 cp bigfile.tar s3://mybucket/backups/2026/bigfile.tar
aws s3 ls s3://mybucket/backups/
注意 backups/2026/ 看起来像目录,其实只是键名里的斜杠——对象存储没有真正的目录,所谓的"目录"是前缀匹配模拟出来的。这个设计上的取舍带来了两个后果:
- 好处:没有目录树就没有集中式元数据瓶颈,可以横向扩展到万亿级对象
- 坏处:不能原地改一个字节,不能 rename(rename 等于复制加删除),也没有 POSIX 锁
对象存储的典型场景:备份归档、图片视频、日志、数据湖、容器镜像仓库的后端。
- 应用要一块独占的盘、自己管布局 → 块
- 多个客户端要共享同一份数据、要目录和 POSIX 语义 → 文件
- 一次写入多次读取、按名字整存整取、量非常大 → 对象
判断依据不是"哪个性能高",而是"应用的访问模式是什么"。用错语义,后面的调优会一直很别扭。
协议地图
同一种语义可以由不同协议承载,接入时要分清:
块语义 ──┬─ iSCSI 成熟、通用、开销偏大
├─ NVMe-oF 高性能,需要 RDMA 或高质量 TCP 网络
└─ RBD Ceph 原生,内核或 librbd 客户端
文件语义 ──┬─ NFS 跨平台通用,v3 无状态 / v4 有状态
├─ SMB Windows 生态
├─ CephFS Ceph 原生,内核或 FUSE 客户端
└─ GPFS 需装客户端软件,性能最好
对象语义 ──┬─ S3 事实标准
└─ Swift OpenStack 生态,新项目基本不再选
某团队要为一套 MySQL 主从集群准备存储,单实例数据量 2TB,要求低延迟。最合适的是?
关于对象存储,下面哪些说法是对的?(多选)
这节课的落点
- 块、文件、对象是三种访问语义,不是三种产品;Ceph 在一个 RADOS 上翻译出三种
- 块最快但单挂载,文件最好用但元数据是瓶颈,对象最能扩展但语义最弱
- 小文件场景对共享文件系统的元数据压力是主要风险点
- 选型看应用的访问模式,而不是看哪个听起来更高级
延伸资料
- ·k8s-in-action
storage/README.md - ·Storplan 容量与性能规划 ↗