JuiceFS 源码解析系列
元数据
管理海量文件的元数据
一种是将所有文件的元数据都加载到内存中,如 HDFS 的 NameNode。
一种是仅缓存部分元数据在内存,如 CephFS 的 MDS。当请求的元数据不在缓存中时,MDS 需要暂存该请求,并通过网络从硬盘(元数据池)中读取相应内容,解析后再进行重试。
JuiceFS 采用的是第一种全内存方案,并通过不断的优化来减小文件元数据的内存占用。全内存模式通常会使用实时落盘的事务日志来保证数据的可靠性,JuiceFS 还使用了 Raft 共识算法来实现元数据的多机复制和自动故障切换。
快速处理元数据请求
元数据引擎的关键性能指标是每秒能处理的请求数量。类似于 Redis 的无锁模式,所有核心数据结构的相关操作都在单个线程中执行。
多分区水平扩展
通过聚合分布在多个节点的虚拟分区中的元数据来实现水平扩展,以支撑更大的数据规模和更高的性能需求。 - 每个分区各自负责文件系统中的一部分子树,由客户端来协调和管理多个分区中的文件,把它们组装成单一的命名空间;同时这些文件能够在多个分区间根据需要进行动态迁移。
内存优化
Arena 自定义内存池绕过 Go 原生 GC 做手动管理: - 降低GC:大块内存批量分配 + 裸指针屏蔽 GC 扫描,减少标记遍历对象数量 - 缩小元数据平均大:取消结构体对齐填充、砍掉 string/slice 运行时头、取消独立对象头、消除内存碎片。
大文件和小文件的内存格式不一样,通过类似 union 的形式,降低小文件的元数据大小。JuiceFS 为支持大文件随机读写,把文件元数据设计为 3 级索引:
- fnode:文件核心信息(大小、权限、inode 等,文件 “身份证”)
- chunks 数组:大文件会切为多个 64MB chunk,用数组存 chunk 索引
- slice:chunk 内的实际数据块,存 ID、长度等,存在哈希表中
小文件(≤64MB)只有 1 个 chunk,chunk 里只有 1 个 slice,slice 长度 = 文件长度。
- 文件变大后,小文件紧凑格式会自动转换?
常见分布式文件系统的单文件内存占用情况如下:
- HDFS:370 字节(数据来源:网络文章[1])
- CephFS:2700 字节(数据来源:Nautilus 版本集群监控 - 32G 内存 1200万文件)
- Alluxio (Heap模式):2100 字节(数据来源:官方文档[2] - 64G 内存 3000万文件)
- JuiceFS 社区版 Redis 引擎:430 字节(数据来源:官方文档[3])
- JuiceFS 企业版:100 字节(数据来源:线上集群监控 - 30G 内存 3亿文件)