1. Direct IO
Direct IO 绕过了操作系统的页缓存(page cache),直接与硬件设备进行数据交互。
Direct IO 的特点:
- 新数据多,不需要缓存
- 内存占用少
- 大文件顺序读写
对于超过阈值(默认 1MB)的同步读取操作,3FS 的客户端会将其转为 AIO (以 Direct IO 方式打开文件)操作以提高读取性能。
使用 Direct IO 时,会涉及到 Block Size 对齐的问题。
2. RDMA
RDMA(Remote Direct Memory Access)是一种数据传输技术,它允许数据在不同计算机之间直接传输,而无需 CPU 的干预。
RDMA 的特点:
- 低 CPU 使用率
- 高吞吐量和低延迟
- 能扩大存储系统的规模
在经过性能测试后,3FS 发现 RDMA Read 每次读取 64KB 数据时,效果最好,因此将小的请求合并,将大的请求分割。
3. CRAQ
CRAQ 是 3FS 的核心设计,它是一种链式结构,用于处理读写请求,全称 Chain Replication with Apportioned Queries。
数据的写入流程:
- 客户端向头部发送写请求
- 写操作沿着链式结构传递,当通过副本时,副本为该对象创建一个新的 dirty 副本
- 当尾部接受到写请求,会为该对象创建一个新的 clean 副本,并沿着链反向确认
- 接受到的副本,会将最新的对象版本标记为 clean,并删除该对象的所有先前副本
数据的读取流程:
不同于标准链式复制,所有读取都转到 Tail Target。CRAQ 可以从任何存储目标读取数据。
但可能会出现以下的读取 dirty 副本的情况:
当读到 dirty 副本时,会向尾部节点进行版本查询,找到一个 clean 副本,然后返回给客户端。
4. 相关组件
- MGMTD 集群管理器
元数据和存储服务向集群管理器发送心跳。
如果部署多个 MGMTD,其中一个会被选为主 MGMTD,当主 MGMTD 发生故障时,另一个 MGMTD 会提升为主 MGMTD。
- Meta 元数据服务
Meta 是无状态的,元数据存储在 FoundationDB 中。文件元数据的操作(打开或创建文件、目录)都会被发送到实现文件系统语义的元数据服务。
- Storage 存储服务
Storage 服务管理本地的 SSD,对外提供存储块接口。存储块实现了 CRAQ 协议,用于处理读写请求。
- Client 客户端
有两种使用方式,一个是 Fuse 客户端,另一个是直接使用 3FS 的 USRBIO 接口。
FUSE 客户端在内核与用户空间传输数据,消耗内存带宽、增加端到端的延时;同时在高并发情况下,FUSE 客户端存在锁竞争问题,限制了性能的扩展。
如果有改造应用端的能力,用户进行可以通过 USRBIO API 直接提交 IO 请求到 FUSE 进程的 I/O 队列,避免像普通 FUSE 客户端那样在内核与用户空间传输数据。
5. Target 的放置
Target 和 Chain 是维护 3FS 的关键对象。假设有 6 个节点:A,B,C,D,E,F,每个节点有 1 块 SSD,每个 SSD 上创建 5 个存储 target:1,2,…,5,那么一共有 30 个 target:A1,A2,A3,…,F5,如果每个 chunk 有 3 个 replica,那么构建链表如下。
Chain | Version | Target 1 (head) | Target 2 | Target 3 (tail) |
---|---|---|---|---|
1 | 1 | A1 | B1 | C1 |
2 | 1 | D1 | E1 | F1 |
3 | 1 | A2 | B2 | C2 |
4 | 1 | D2 | E2 | F2 |
5 | 1 | A3 | B3 | C3 |
6 | 1 | D3 | E3 | F3 |
7 | 1 | A4 | B4 | C4 |
8 | 1 | D4 | E4 | F4 |
9 | 1 | A5 | B5 | C5 |
10 | 1 | D5 | E5 | F5 |
每个 Chain 都有一个版本编号,当 Target 的数据发生变化时,版本号会递增。
一个 Chain 就是一个文件的存储链,不同的 Chain 可以用来存储不同类型的文件。
6. 存储块的分配
在每个 SSD 上,由固定数量的数据文件和一个 RocksDB 数据库组成。
数据文件大小以 2 的幂为增量,从 64KB 到 64MB 不等,总共有 11 个不同的大小。每次分配时,会选择最接近的大小进行分配。分配器为每个大小构建一个包含 256 个文件的资源池,当资源池满时,会创建新的 256 个文件。