[云原生/分布式/文件系统] JuiceFS:云原生环境下的高性能 POSIX 分布式文件系统(可与Lance/Iceberg等数据湖融合)

1 概述:JuiceFS

产品介绍

  • JuiceFS 是一款基于 Apache License 2.0 协议开源的高性能 POSIX 文件系统,专为云原生环境特别优化设计。它采用数据与元数据分离的架构:【文件数据】持久化存储到【对象存储】(如 Amazon S3、阿里云 OSS 等),而【元数据】则持久化到多种兼容的【数据库引擎】中(如 Redis、MySQL、TiKV 等)。

image

image

  • 产品定位:面向云原生环境的分布式共享文件系统,旨在将海量云端存储直接对接到大数据、机器学习、人工智能以及各类应用平台的生产环境中

image

  • 诞生的背景与原因

    • 传统分布式文件系统(如 HDFS、CephFS)在云环境下部署复杂、成本高昂,且与对象存储的衔接不自然
    • 对象存储虽然成本低、容量无上限,但缺乏 POSIX 文件系统语义,无法直接被传统应用使用
    • 市场上缺少一种既兼容 POSIX 语义、又能充分利用对象存储成本优势的方案
    • JuiceFS 的设计灵感来自 Google File SystemHDFSMooseFS,旨在填补这一空白
  • 解决的核心问题

    • 存储成本:利用廉价的对象存储作为数据层,大幅降低存储成本
    • 共享访问:支持数千客户端同时读写,提供强一致性
    • POSIX 兼容:让现有应用无需修改即可使用云端存储
    • 多云/混合云:支持 30+ 种对象存储,适配公有云、私有云和混合云
  • URLs

基于云原生环境

  • JuiceFS 的"云原生"体现在四个层面:

不自带存储、以【对象存储】为底座、以【容器】生态为首选运行环境、【跨云】可迁移——这就是 JuiceFS 的云原生本质。

维度 云原生体现
存储解耦 数据与元数据分离,数据层复用云上已有的对象存储(S3/OSS/COS 等 30+ 种),不自带存储池,天然适配云的弹性按量付费模型
K8s 原生 提供成熟的 CSI Driver(下载量 500 万+),作为 PersistentVolume 直接挂载,支持 Sidecar、Mount Pod 共享、Operator 缓存预热等云原生范式
多协议弹性接入 FUSE / Hadoop SDK / CSI / S3 Gateway / WebDAV / Python SDK,容器、大数据、AI 框架各取所需,无状态客户端可随时拉起/销毁
多云/混合云无锁定 同一文件系统可跨可用区、跨云厂商共享,客户端可弹性扩缩容,不绑定特定基础设施

发展历程

时间 里程碑
2021 年 1 月 JuiceFS 正式开源,以 Apache 2.0 协议发布(最初为 AGPLv3)
2022 年 1 月 开源协议从 AGPLv3 更改为 Apache 2.0(PR #1282),大幅降低使用门槛
v1.0 首个正式 GA 版本,核心功能趋于稳定,存储格式定型
v1.1 新增 --storage-class--update-fstab、目录统计、--open-cache-limit 等功能
v1.2 S3 Gateway 支持多用户管理;引入 ACL、--upload-hours--backup-skip-trash
v1.3(LTS) 2025 年发布,继开源以来第四个重要版本。支持 Python SDK、Windows 客户端大幅优化、1 亿文件备份分钟级完成、集成 Apache Ranger、SQL/TiKV 元数据引擎性能提升
v1.4(LTS) 2026 年 7 月发布,第五个主要版本,维护 24 个月。引入存储分层用户/组配额管理可恢复同步检查点机制元数据操作变更日志、CIFS/SMB 支持、Hadoop Kerberos 认证、SM 加密支持等企业级特性
2025 年度 社区版数据量增长 89% 超 1.3 EB;GitHub Star 超 12.6K;文件系统 590K+,活跃客户端 150K+,文件数量 4000 亿+;营收连续第三年 100% 增长
企业版 5.2 2025 年上半年发布,单卷突破千亿文件规模,1 毫秒元数据时延;100 台 100Gbps 节点聚合读带宽达 1.2 TB/s
企业版 5.3 2025 年下半年发布,分区限制从 256 提升至 1024,支持单卷超 5,000 亿文件;首次引入 RDMA 技术(实验阶段)

主要功能

文件系统核心功能

  • 完全 POSIX 兼容:通过 8813 项 pjdfstest 兼容性测试,可作为本地文件系统无缝对接现有应用
  • 强一致性:确认的修改在所有挂载服务器立即可见,提供 close-to-open 一致性模型
  • 全局文件锁:支持 BSD locks(flock)和 POSIX record locks(fcntl)
  • 扩展属性(xattr):支持文件扩展属性
  • POSIX ACL:v1.2 起支持 POSIX ACL 细粒度权限管理
  • 原子操作:原子的 rename 和元数据操作
  • Mmap 支持:完整的 mmap 读写支持
  • Fallocate punch hole:支持 punch hole 操作
  • 硬链接与符号链接:完整支持

多协议接入

接入方式 说明
FUSE 挂载 POSIX 兼容方式挂载到 Linux/macOS/Windows,作为本地文件系统使用
Hadoop Java SDK 兼容 Hadoop 2.x 和 3.x,可直接替代 HDFS
Kubernetes CSI Driver 为 K8s 集群提供海量持久化存储
S3 Gateway 提供 S3 兼容接口,支持 AWS CLI、s3cmd、MinIO client 等工具
WebDAV 通过 HTTP 协议以 RESTful API 方式接入
Python SDK v1.3 新增,原生实现 fsspec,便于接入 Ray 等框架
Docker Volume 作为 Docker 持久卷使用

数据管理与安全

  • 数据压缩:支持 LZ4 或 Zstandard 压缩,在 Block 上传前进行
  • 数据加密:支持传输中加密和静态加密(RSA + AES256-GCM),v1.4 新增 SM 国密加密
  • 回收站:默认开启,删除文件后保留指定天数,支持恢复
  • 配额管理:目录级配额;v1.4 新增用户级和组级配额
  • 存储分层:v1.4 引入多级存储分层,支持配置不同存储类
  • 数据备份:元数据自动备份到对象存储,1 亿文件分钟级完成
  • Apache Ranger 集成:支持大数据场景的细粒度权限管理
  • 快照:在路线图中规划

运维与监控

  • 内置基准测试:提供子命令运行性能基准测试
  • Prometheus 监控:支持 Prometheus 指标导出和 remote_write 协议
  • Consul 集成:支持服务发现
  • 匿名使用数据:默认收集匿名使用数据,可通过 --no-usage-report 禁用

核心优势

  1. 极高的 POSIX 兼容性:通过全部 8813 项 pjdfstest 测试,几乎所有不依赖特定文件系统的应用都能直接运行

  2. 存储成本极低:利用对象存储作为数据层,成本仅为传统分布式文件系统的一小部分。

    1. 例如,百图生科使用 JuiceFS 后存储成本降低 90%
  3. 卓越的性能表现

    • 元数据访问延迟低至 1-3 毫秒(企业版千亿文件规模下保持 1 毫秒)
    • 数据访问延迟取决于对象存储(通常 20-100 ms)
    • 顺序读写吞吐量可达 50 MiB/s 至 2800 MiB/s
    • 相比 EFS 和 S3FS,提供 10 倍以上的吞吐量
    • 企业版在 100 台 100Gbps 节点下聚合读带宽达 1.2 TB/s
  4. 多元数据引擎支持:Redis、MySQL/MariaDB、PostgreSQL、SQLite、TiKV、FoundationDB、etcd 等,灵活适配不同规模和场景

  5. 海量对象存储兼容:支持 30+ 种对象存储,包括所有主流公有云和私有化方案

  6. 覆盖写性能优异:采用追加写新 Block + 更新元数据的方式,避免 CephFS 式的读-改-写开销

  7. 云原生生态完善:CSI Driver 下载量超 500 万次,Kubernetes 集成成熟

  8. 开源协议友好:Apache 2.0 协议,无商业使用限制

  9. 跨平台支持:Linux、macOS、Windows 全平台覆盖,支持 x86、ARM 等多种架构

  10. AI 场景深度适配:Python SDK + fsspec 兼容、分布式缓存、海量小文件优化

主要短板

  1. 随机写性能有待提升:随机写会产生文件碎片(多个重叠 Slice),影响读性能。虽然后台会异步运行碎片合并,但短时间内大量覆盖写仍可能导致性能下降
  2. 分布式缓存仅限企业版:社区版不支持分布式缓存功能,大规模 AI 训练场景下可能需要企业版
  3. 元数据引擎运维负担:需要额外维护一个元数据引擎(Redis/MySQL/TiKV 等),增加了系统复杂度
  4. 单文件系统绑定单个对象存储:不支持一个文件系统同时绑定多个不同的对象存储(虽然支持同一对象存储的多个 bucket 分片)
  5. 无法直接访问对象存储已有数据:JuiceFS 有自己的数据组织格式,不能直接读取对象存储中已有的原始文件,需要通过 juicefs sync 迁移
  6. FUSE 性能开销:作为用户态文件系统,FUSE 层会引入少量性能开销
  7. 压缩算法不可更改:一旦格式化时设定压缩算法,后续不可修改,否则会导致读取已有数据失败
  8. 加密密钥不可更改:RSA 私钥设定后不可更改
  9. Windows 平台需额外依赖:需安装 WinFsp 才能实现 FUSE 挂载
  10. macOS 平台需额外依赖:需安装 macFUSE 才能实现 FUSE 挂载

局限性

  1. 不适合作为块存储使用:JuiceFS 是文件系统,不提供块设备接口
  2. 不适合超低延迟场景:由于数据层依赖对象存储,数据访问延迟(20-100 ms)无法达到本地 NVMe 或全闪存分布式文件系统的水平
  3. 不适合完全离线环境:需要对象存储作为数据层,纯离线场景下需自建 MinIO/Ceph 等对象存储
  4. 社区版无快照功能:快照功能仍在路线图中,目前仅企业版有类似能力
  5. 大目录 listing 性能:单目录内大量文件时,listing 性能受限于元数据引擎能力
  6. 对象存储 API 调用费用:大量小文件场景下,对象存储 API 调用次数可能带来额外费用
  7. 会话管理开销:客户端需要定期发送心跳维护会话,在极高并发下元数据引擎压力较大

适用场景

强烈推荐

  • AI/ML 训练与推理:海量训练数据存储、模型文件管理、checkpoint 存储。已有大量成功案例(智谱 AI、阶跃星辰、携程、百图生科等)
  • 大数据分析:替代 HDFS 提供低成本存储,兼容 Hadoop 生态
  • Kubernetes 持久化存储:为容器化应用提供共享存储,CSI Driver 成熟
  • 多云/混合云存储:跨可用区、跨云共享文件系统
  • 自动驾驶数据管理:亿级文件存储(九识智能、酷睿程等案例)
  • 生命科学/生物信息:大模型训练数据平台(百图生科案例)
  • 量化投资:高性能存储平台(Ariste AI 使用 JuiceFS + MinIO)

适合使用

  • 日志归档:低成本长期存储
  • 媒体处理:视频/图片文件的共享读写
  • DevOps 文件共享:CI/CD 流水线中的构建产物共享
  • 科学计算:Ray/MPI 等框架的数据存储

不太适合

  • 数据库存储:需要超低延迟和块设备接口
  • 高频小文件随机写:会产生大量碎片,虽然有后台合并但仍有性能影响
  • 单机本地存储:如果不需要共享和海量容量,本地文件系统更简单高效

同类竞品

特性 JuiceFS CephFS MinIO SeaweedFS AWS EFS Alluxio
定位 云原生 POSIX 文件系统 统一分布式存储系统 高性能对象存储 分布式文件+对象存储 托管 NFS 文件系统 数据编排与缓存层
数据存储 对象存储(30+种) RADOS Pool(自管) 对象存储(自管) 本地磁盘/卷 AWS 托管 多源(对接现有存储)
元数据引擎 Redis/MySQL/TiKV/SQL 等 MDS(自管) 内置 Volume Manager AWS 托管 内置
POSIX 兼容 ✓(8813 项测试) ✗(S3 API) 部分 ✓(NFS) ✓(FUSE)
Hadoop 兼容
S3 兼容 ✓(Gateway) ✓(RGW) ✓(原生) ✓(S3 API)
K8s CSI ✓(EFS CSI)
数据压缩 ✓(LZ4/Zstd) ✓(BlueStore 层)
数据加密 ✓(RSA+AES/SM) ✓(Messenger v2) ✓(托管) 取决于后端
客户端缓存 ✓(核心能力)
覆盖写性能 优秀(追加+元数据更新) 较差(读-改-写) N/A 一般 一般 取决于后端
快照 规划中 ✓(版本控制) 取决于后端
开发语言 Go C++ Go Go 托管 Java
开源协议 Apache 2.0 LGPL v2.1/v3 AGPLv3 Apache 2.0 闭源 Apache 2.0
部署复杂度 中(需元数据引擎+对象存储) 高(完整 Ceph 集群) 极低(托管)

与 CephFS 的关键区别

  • 架构理念:CephFS 是一套完整且独立的系统,所有数据存储在自有 RADOS Pool 中;JuiceFS 是一个轻量级客户端,复用已有对象存储和数据库
  • 覆盖写:CephFS 需直接修改对应 objects,EC/压缩时还需先读后写,开销大;JuiceFS 将更新数据作为新 objects 写入并更新元数据即可,性能大幅提升
  • 数据压缩:CephFS 依赖 BlueStore 层压缩;JuiceFS 在 Block 上传前压缩,甚至可以对接 RADOS 实现双重压缩
  • 数据加密:CephFS 依赖 Messenger v2 和 OSD 层加密;JuiceFS 在客户端侧加解密,对对象存储完全透明
  • 部署模式:CephFS 适合私有云部署;JuiceFS 适合公有云、私有云、混合云

发展趋势

开源社区活跃趋势

  • GitHub Star:截至 2025 年底超 12.6K,持续稳定增长
  • 下载量:JuiceFS 客户端下载量超 5 万次,CSI Driver 下载量超 500 万次
  • 社区规模:中文社区 10 个微信群组,Slack 英文社区超千人
  • 贡献者:2025 年有 60 位贡献者参与,新增 305 个 Issue,合并 601 个 PR
  • v1.4 参与度:57 位贡献者贡献了 520 次提交
  • 用户增长
    • 文件系统数量 590K+,年增长 82%
    • 活跃客户端 150K+,年增长 46%
    • 文件数量 4000 亿+,年增长 43%
    • 数据总量 1.3 EiB+,年增长 89%
  • 营收增长:连续第三年 100% 增长,为社区持续投入提供保障

Star 与 Fork 趋势

  • Star 数从开源初期的快速增长,到目前进入稳步增长阶段
  • Fork 数稳定增长,反映社区参与度持续提升
  • CSI Driver 的下载量(500 万+)远超客户端本身(5 万+),表明 Kubernetes 生态是最大的使用入口

项目发展趋势

JuiceFS 正从开源新秀成长为 AI 时代最受信任的云原生文件系统之一,社区版持续聚焦通用性与 AI 场景适配,企业版向千亿甚至 5,000 亿文件规模迈进,整体生态高速增长、社区活跃度持续攀升。

2 工作原理与架构

概念术语

术语 说明
Volume(文件系统) 一个独立的 JuiceFS 命名空间,由格式化创建
Client(客户端) JuiceFS 挂载进程,协调元数据引擎和对象存储,实现文件系统接口
Metadata Engine(元数据引擎) 存储文件元数据的数据库引擎,如 Redis、MySQL、TiKV 等
Data Storage(数据存储) 存储文件数据的对象存储或本地磁盘
Chunk 文件分割的逻辑单位,固定 64 MiB,用于大文件快速定位
Slice 数据写入的逻辑单位,变长,每次连续写入分配一个 Slice,不能跨越 Chunk 边界
Block 文件分割后的物理存储最小单位,默认 4 MiB(可配置 64 KiB ~ 16 MiB),与对象存储中的 object 一一对应
SliceRef Slice 的引用计数记录,用于垃圾回收
Session 客户端会话,通过心跳维护,只读客户端不记录会话
Trash(回收站) 删除文件的暂存区,默认保留 1 天
Compaction(碎片合并) 将同一 Chunk 内多个重叠的 Slice 合并为更少、更连续的 Slice
FUSE Filesystem in Userspace,用户态文件系统接口
Writeback(写缓存) 数据先写入本地缓存,再异步上传到对象存储

架构与运行原理

技术架构(必读)

image

https://juicefs.com/docs/zh/community/architecture

整体架构

graph TB subgraph "JuiceFS 架构" Client["JuiceFS Client<br/>(FUSE / Hadoop SDK / CSI / S3 Gateway / WebDAV / Python SDK)"] subgraph "元数据引擎 Metadata Engine" Redis["Redis"] MySQL["MySQL/MariaDB"] PostgreSQL["PostgreSQL"] SQLite["SQLite"] TiKV["TiKV"] Etcd["etcd"] FoundationDB["FoundationDB"] end subgraph "数据存储 Data Storage" S3["Amazon S3"] OSS["阿里云 OSS"] COS["腾讯云 COS"] GCS["Google Cloud Storage"] Azure["Azure Blob"] MinIO["MinIO"] CephRGW["Ceph RGW"] Local["本地磁盘"] end end Client <-->|"元数据读写"| Redis Client <--> MySQL Client <--> PostgreSQL Client <--> SQLite Client <--> TiKV Client <--> Etcd Client <--> FoundationDB Client <-->|"数据读写(Block 级别)"| S3 Client <--> OSS Client <--> COS Client <--> GCS Client <--> Azure Client <--> MinIO Client <--> CephRGW Client <--> Local

JuiceFS 由三个核心部分组成:

  1. JuiceFS Client(客户端):所有文件读写、碎片合并、回收站文件过期删除等后台任务均在客户端中发生。客户端同时与对象存储和元数据引擎打交道,支持 FUSE、Hadoop SDK、CSI、S3 Gateway、WebDAV、Python SDK 等多种接入方式
  2. Data Storage(数据存储):文件数据被切分后上传至对象存储。支持几乎所有公有云对象存储,以及 MinIO、Ceph RGW 等私有化方案
  3. Metadata Engine(元数据引擎):存储文件元数据,包括常规文件系统元数据(文件名、大小、权限、时间、目录结构等)和文件数据索引(数据分配和引用计数、客户端会话等)

文件存储机制:Chunk → Slice → Block

graph LR subgraph "逻辑结构" File["📄 文件 (160 MiB)"] Chunk0["Chunk 0\n(0-64 MiB)"] Chunk1["Chunk 1\n(64-128 MiB)"] Chunk2["Chunk 2\n(128-160 MiB)"] Slice0["Slice\n(一次连续写入)"] Slice1["Slice\n(一次连续写入)"] Slice2["Slice\n(一次连续写入)"] end subgraph "物理存储" B0["Block 0\n(4 MiB)"] B1["Block 1\n(4 MiB)"] B2["Block 2\n(4 MiB)"] Bn["Block N\n(4 MiB)"] ObjStore["📁 对象存储\n/chunks/0/0/\nsliceId_index_size"] end File --> Chunk0 File --> Chunk1 File --> Chunk2 Chunk0 --> Slice0 Chunk1 --> Slice1 Chunk2 --> Slice2 Slice0 --> B0 Slice0 --> B1 Slice0 --> B2 Slice0 --> Bn B0 --> ObjStore B1 --> ObjStore B2 --> ObjStore Bn --> ObjStore

Chunk(逻辑分块)

  • 每个文件由 1 或多个 Chunk 组成,每个 Chunk 最大 64 MiB
  • 不论文件多大,所有读写都根据偏移量定位到对应的 Chunk
  • 只要文件总长度不变,Chunk 切分固定不变
  • 作用:优化大文件的查找定位

Slice(逻辑写入单位)

  • 一个 Slice 代表一次连续写入,隶属于某个 Chunk,不能跨越 Chunk 边界
  • 顺序写入一个 160 MiB 文件 → 3 个 Chunk,每个 Chunk 仅包含 1 个 Slice
  • 多次追加写(每次 flush)→ 每个 Chunk 可能包含多个 Slice
  • 相同区域反复修改 → Slice 之间发生重叠
  • 读取规则:对于每一处文件位置,读到该位置最新写入的 Slice("从上往下看")
  • 碎片化问题:大量重叠 Slice 会显著影响读性能,客户端会异步运行碎片合并

Block(物理存储单位)

  • Slice 在持久化时被进一步拆分为 Block,默认 4 MiB(可配置 64 KiB ~ 16 MiB)
  • 多线程并发上传以提升写性能
  • Block 是对象存储和磁盘缓存的最小存储单元
  • 对象命名格式:${fsname}/chunks/${hash}/${sliceId}_${index}_${size}

在对象存储中看不到原始文件,只有 chunks 目录和一堆数字编号的目录和文件。这些数字编号的对象就是经过 JuiceFS 拆分存储的 Block,而 Block 与 Chunk、Slice 的对应关系存储在元数据引擎中。

元数据结构

核心数据结构

结构 说明 关键字段
Setting 格式化信息 Name, UUID, Storage, Bucket, BlockSize, Compression, TrashDays, Shards 等
Counter 计数器与任务时间戳 usedSpace, totalInodes, nextInode, nextChunk, nextSession, nextTrash, 各种 cleanup 时间戳
Session 客户端会话 SID 和超时时间,定时心跳更新
SessionInfo 会话详情 版本、主机名、挂载点路径、进程 PID
Node (Attr) 文件属性 Typ, Mode, Uid, Gid, Atime, Mtime, Ctime, Nlink, Length, Parent 等
Edge 目录边 parentInode + name → type + inode
Chunk 数据块元数据 inode + index → []Slices
SliceRef Slice 引用计数 sliceId + size → refs(大部分 refs=1,以 refs-1 存储)
DelFiles 待清理文件 inode + length → expire
DelSlices 延迟删除的 Slices sliceId + deleted → []slice
Sustained 会话临时保留文件 sid → []inode

Slice 结构

type Slice struct {
    Pos  uint32 // Slice 在 Chunk 中的偏移位置
    ID   uint64 // Slice 的全局唯一 ID
    Size uint32 // Slice 的总大小
    Off  uint32 // 有效数据在此 Slice 中的偏移位置
    Len  uint32 // 有效数据在此 Slice 中的大小
}

每个 Slice 编码为 24 字节二进制数据。通过 OffLen 标记有效数据范围,告诉文件系统每个 Slice 中哪些部分是有效的。

三种元数据引擎的存储映射

结构 Redis SQL TKV (TiKV)
Setting setting (String) jfs_setting setting key
Node i${inode} (String) | jfs_node 表 | A${inode}I key
Edge d${inode} (Hash) | jfs_edge 表 | A${inode}D${name} key
Chunk c${inode}_${index} (List) jfs_chunk A${inode}C${index} key
SliceRef sliceRef (Hash) jfs_chunk_ref K${sliceId}${size} key
DelFiles delfiles (Sorted Set) jfs_delfile D${inode}${length} key
Sustained session${sid} (List) | jfs_sustained 表 | SS${sid}${inode} key

数据读写流程

写流程

sequenceDiagram participant App as 应用程序 participant FUSE as FUSE 层 participant Client as JuiceFS Client participant Meta as 元数据引擎 participant Obj as 对象存储 App->>FUSE: write(data, offset) FUSE->>Client: 转发写请求 Note over Client: 1. 根据 offset 定位 Chunk Note over Client: 2. 创建/追加 Slice Note over Client: 3. 数据写入内存缓冲区 Client->>Client: flush 触发上传 Note over Client: 4. Slice 拆分为多个 Block (4 MiB) par 多线程并发上传 Client->>Obj: PUT Block 0 Client->>Obj: PUT Block 1 Client->>Obj: PUT Block N end Client->>Meta: 更新 Chunk 元数据<br/>(Slice 信息) Meta-->>Client: 确认 Client-->>FUSE: 写完成 FUSE-->>App: 写成功

读流程

sequenceDiagram participant App as 应用程序 participant FUSE as FUSE 层 participant Client as JuiceFS Client participant Meta as 元数据引擎 participant Cache as 本地缓存 participant Obj as 对象存储 App->>FUSE: read(offset, length) FUSE->>Client: 转发读请求 Client->>Client: 1. 根据 offset 定位 Chunk alt 缓存命中 Client->>Cache: 查询本地缓存 Cache-->>Client: 返回缓存数据 else 缓存未命中 Client->>Meta: 2. 查询 Chunk 的 Slice 列表 Meta-->>Client: 返回 Slices (含重叠) Note over Client: 3. 重构最新版数据分布<br/>(后加入的 Slice 优先) Client->>Obj: 4. 读取对应的 Block 对象 Obj-->>Client: 返回 Block 数据 Client->>Cache: 写入本地缓存 end Client-->>FUSE: 返回数据 FUSE-->>App: 读成功

Slice 重构机制(读取时的关键操作)

当多个 Slices 有重叠时,读取需顺序遍历 Slices 列表并重构最新版数据分布:

  1. 有多个 Slice 覆盖的部分以最后加入的 Slice 为准
  2. 没有被 Slice 覆盖的部分自动补零(sliceId = 0)
  3. 根据文件 size 截断 Chunk

示例:假设 Chunk 0 中有 3 个 Slices:

Slice{pos: 10M, id: 10, size: 30M, off: 0, len: 30M}  // 第一次写入
Slice{pos: 20M, id: 11, size: 16M, off: 0, len: 16M}  // 第二次写入(覆盖部分)
Slice{pos: 16M, id: 12, size: 10M, off: 0, len: 10M}  // 第三次写入(覆盖部分)

图示(每个 _ 表示 2 MiB):
   Chunk: |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|
Slice 10:           |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|
Slice 11:                     |_ _ _ _ _ _ _ _|
Slice 12:                 |_ _ _ _ _|

重构后:  |_ _ _ _ _|_ _ _|_ _ _ _ _|_ _ _ _ _|_ _|_ _ _ _ _ _ _ _ _ _ _ _|
              0      10      12         11    10             0

垃圾回收机制

flowchart TD A["文件删除请求"] --> B{回收站开启?} B -->|是| C["移动到回收站目录\n(按小时创建子目录)"] C --> D["后台任务检查过期文件"] D --> E["标记为待删除 delfile"] B -->|否| F{文件被其他程序使用?} F -->|是| G["标记为暂缓删除 sustained\n等待程序关闭"] F -->|否| E E --> H["放入删除队列 maxDeleting"] H --> I["查找文件对应的所有 Chunk"] I --> J["删除 Chunk,减少 Slice 引用计数"] J --> K{Slice refs = 0?} K -->|是| L["成为 Pending Deleted Slices"] K -->|否| M["保留"] L --> N["后台清理对象存储中的数据片段"]

碎片合并(Compaction)

  • 触发条件:文件被多次写入导致同一 Chunk 内出现多个重叠的 Slice
  • 合并过程:将旧 Slices 合并压缩为新 Slice,旧 Slices 标记为删除
  • 回收站集成:若开启回收站,被 compact 删除的旧 Slices 通过 DelSlices 记录,保留 TrashDays
  • 自动触发:每次读/写文件时都会检查,并在后台任务中进行该文件相关碎片的整理

数据压缩与加密

原始数据 → [压缩 (LZ4/Zstd)] → [加密 (RSA+AES256-GCM/SM)] → 上传到对象存储
  • 压缩:在 Block 上传前进行,对象名不变,内容为压缩后结果,不携带元信息
  • 加密:对象名不变,内容为 header + 加密数据,Header 记录经 RSA 加密的对称密钥 + 随机种子
  • 不可逆:压缩算法和 RSA 密钥一旦设定不可更改

FUSE 实现

  • 使用 low-level API(基于 inode),而非 high-level API(基于路径)
  • 底层依赖 go-fuse,而非 libfuse
  • 选择 low-level API 的原因:JuiceFS 的元数据按 inode 组织,直接提供基于 inode 的 API,避免 inode↔path 的反复转换开销

多级缓存机制

缓存层 说明 配置参数
元数据缓存 属性缓存、目录项缓存、打开文件缓存 --attr-cache, --entry-cache, --dir-entry-cache, --open-cache
数据读缓存 本地磁盘缓存 Block 数据,主动失效 --cache-dir, --cache-size, --free-space-ratio
写缓存(Writeback) 数据先写本地缓存,异步上传对象存储 --writeback, --buffer-size
预读 并发预读后续 Block --prefetch, --max-readahead

3 使用指南

安装部署

Linux 安装

方式一:一键安装脚本(推荐)

# 默认安装到 /usr/local/bin
curl -sSL https://d.juicefs.com/install | sh -

# 安装到自定义位置(如 /tmp)
curl -sSL https://d.juicefs.com/install | sh -s /tmp

方式二:下载预编译二进制

# 1. 获取最新版本号
JFS_LATEST_TAG=$(curl -s https://api.github.com/repos/juicedata/juicefs/releases/latest | grep 'tag_name' | cut -d '"' -f 4 | tr -d 'v')

# 2. 下载客户端
wget "https://github.com/juicedata/juicefs/releases/download/v${JFS_LATEST_TAG}/juicefs-${JFS_LATEST_TAG}-linux-amd64.tar.gz"

# 3. 解压安装包
tar -zxf "juicefs-${JFS_LATEST_TAG}-linux-amd64.tar.gz"

# 4. 安装客户端
sudo install juicefs /usr/local/bin

# 5. 验证安装
juicefs --help

方式三:包管理器

# Ubuntu PPA
sudo add-apt-repository ppa:juicefs/ppa
sudo apt-get update
sudo apt-get install juicefs

# Fedora Copr
sudo dnf copr enable -y juicedata/juicefs
sudo dnf install juicefs

# Snap
sudo snap install juicefs
# 解除 Snap 沙箱对 FUSE 的限制
sudo ln -s -f /snap/juicefs/current/juicefs /snap/bin/juicefs

# Arch AUR
yay -S juicefs

Windows 安装

前置条件:需先安装 WinFsp(开源 Windows 文件系统代理,提供 FUSE 仿真层)

方式一:预编译客户端

  1. GitHub Releases 下载 juicefs-x.y.z-windows-amd64.zip
  2. 解压获得 juicefs.exe
  3. juicefs.exe 移动到 C:\Windows\System32,或放入自定义目录并添加到 PATH

方式二:Scoop

scoop install juicefs

方式三:WSL 中使用 Linux 版客户端

在 WSL 中安装 Linux 版 JuiceFS 客户端,使用方式与 Linux 完全一致。

macOS 安装

前置条件:需安装 macFUSE 才能实现 FUSE 挂载(不使用挂载则无需安装)

# Homebrew 安装
brew install juicefs

# 或下载预编译二进制
# 下载 darwin-amd64 (Intel) 或 darwin-arm64 (M1) 版本
sudo install juicefs /usr/local/bin

Docker 容器

FROM ubuntu:20.04

RUN apt update && apt install -y curl fuse && \
    apt-get autoremove && \
    apt-get clean && \
    rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/*

RUN set -x && \
    mkdir /juicefs && cd /juicefs && \
    JFS_LATEST_TAG=$(curl -s https://api.github.com/repos/juicedata/juicefs/releases/latest | grep 'tag_name' | cut -d '"' -f 4 | tr -d 'v') && \
    curl -s -L "https://github.com/juicedata/juicefs/releases/download/v${JFS_LATEST_TAG}/juicefs-${JFS_LATEST_TAG}-linux-amd64.tar.gz" \
    | tar -zx && \
    install juicefs /usr/bin && \
    cd .. && rm -rf /juicefs

CMD [ "juicefs" ]

源码编译

# 类 Unix 系统
# 依赖:Go 1.20+、GCC 5.4+
git clone https://github.com/juicedata/juicefs.git
cd juicefs
git checkout v1.4.0  # 切换到正式版本
make
# 编译好的 juicefs 二进制程序在当前目录

# Windows 编译
# 依赖:WinFsp、Go 1.20+、MinGW-w64 (GCC)
git clone https://github.com/juicedata/juicefs.git && cd juicefs
mkdir "C:\WinFsp\inc\fuse"
copy .\hack\winfsp_headers\* C:\WinFsp\inc\fuse\
go env -w CGO_CFLAGS=-IC:/WinFsp/inc/fuse
go build -ldflags="-s -w" -o juicefs.exe .

关键操作

1. 格式化文件系统

# 基本格式
juicefs format [options] META-URL NAME

# 示例 1:使用 SQLite(本地测试,最简单)
juicefs format sqlite3://myjfs.db myjfs

# 示例 2:使用 Redis + S3
juicefs format redis://localhost myjfs \
  --storage=s3 \
  --bucket=https://mybucket.s3.us-east-2.amazonaws.com

# 示例 3:使用 MySQL(带密码)
META_PASSWORD=mypassword juicefs format \
  mysql://jfs:@(127.0.0.1:3306)/juicefs myjfs

# 示例 4:开启配额
juicefs format sqlite3://myjfs.db myjfs --inodes=1000000 --capacity=102400

# 示例 5:关闭回收站
juicefs format sqlite3://myjfs.db myjfs --trash-days=0

# 示例 6:开启压缩
juicefs format sqlite3://myjfs.db myjfs --compress=zstd

# 示例 7:开启加密
juicefs format sqlite3://myjfs.db myjfs --encrypt-rsa-key=/path/to/private.pem

# 示例 8:多 bucket 分片
juicefs format sqlite3://myjfs.db myjfs --storage=s3 --bucket=s3://bucket-prefix --shards=4

2. 挂载文件系统

# 基本格式
juicefs mount [options] META-URL MOUNTPOINT

# 前台挂载(用于调试)
juicefs format sqlite3://myjfs.db myjfs
juicefs mount sqlite3://myjfs.db /mnt/jfs

# 后台挂载(生产环境推荐)
juicefs mount redis://localhost /mnt/jfs -d

# 带密码的 Redis 后台挂载(更安全)
META_PASSWORD=mypassword juicefs mount redis://localhost /mnt/jfs -d

# 开启写缓存(提升小文件写入速度)
juicefs mount redis://localhost /mnt/jfs -d --writeback

# 只读模式
juicefs mount redis://localhost /mnt/jfs -d --read-only

# 挂载子目录
juicefs mount redis://localhost /mnt/jfs --subdir /dir/in/jfs

# Windows 挂载
juicefs mount sqlite3://myjfs.db Z:

# 关闭元数据自动备份
juicefs mount redis://localhost /mnt/jfs --backup-meta 0

# 开机自动挂载(写入 /etc/fstab)
# 方法一:手动添加 fstab 条目
echo "redis://localhost /mnt/jfs juicefs _netdev,mode=0755 0 0" | sudo tee -a /etc/fstab
# 方法二:使用 --update-fstab 选项自动添加
juicefs mount redis://localhost /mnt/jfs -d --update-fstab

3. 查看状态

# 查看文件系统状态
juicefs status redis://localhost

# 查看指定会话详情
juicefs status redis://localhost --session 1

# 查看更多统计信息
juicefs status redis://localhost --more

4. 修改配置

# 查看当前配置
juicefs config redis://localhost

# 修改配额
juicefs config redis://localhost --inodes 10000000 --capacity 1048576

# 修改回收站保留天数
juicefs config redis://localhost --trash-days 7

# 限制客户端版本
juicefs config redis://localhost --min-client-version 1.0.0

5. 垃圾回收

# 只检查(不修改)
juicefs gc redis://localhost

# 触发所有 slices 的碎片合并
juicefs gc redis://localhost --compact

# 删除泄漏的对象
juicefs gc redis://localhost --delete

# 同时合并碎片和删除泄漏对象
juicefs gc redis://localhost --compact --delete

# 指定并发线程数
juicefs gc redis://localhost --compact --delete --threads=20

6. 数据同步

# 在 JuiceFS 与对象存储之间同步
juicefs sync /mnt/jfs/data/ s3://my-bucket/backup/

# 从对象存储迁移数据到 JuiceFS
juicefs sync s3://source-bucket/ /mnt/jfs/data/

7. Kubernetes 中使用

# 安装 JuiceFS CSI Driver
helm repo add juicefs-csi-driver https://juicedata.github.io/charts/
helm repo update
helm install juicefs-csi-driver juicefs-csi-driver/juicefs-csi-driver

# 创建 Secret
kubectl create secret generic juicefs-secret \
  --from-literal=metaurl="redis://localhost" \
  --from-literal=storage="s3" \
  --from-literal=bucket="https://mybucket.s3.us-east-2.amazonaws.com" \
  --from-literal=access-key="$ACCESS_KEY" \
  --from-literal=secret-key="$SECRET_KEY"

# 创建 PersistentVolume 和 PersistentVolumeClaim
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: juicefs-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: csi.juicefs.com
    volumeHandle: juicefs-volume-id
    volumeAttributes:
      secretName: juicefs-secret
      juicefsName: myjfs
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: juicefs-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  volumeName: juicefs-pv
EOF

8. S3 Gateway

# 启动 S3 Gateway
juicefs gateway redis://localhost localhost:8010

# 使用 AWS CLI 访问
aws --endpoint-url http://localhost:8010 s3 ls s3://myjfs/

9. 基准测试

# 运行内置基准测试
juicefs bench /mnt/jfs

# 使用 fio 进行详细测试(参考  https://juicefs.com/docs/zh/community/fio )
fio --name=juicefs-test --directory=/mnt/jfs --rw=randread --bs=4k --size=1G --numjobs=1

4 行业案例

CASE Lance 智驾实践 | 卓驭:百 PB 级智驾数据存储架构演进 | 2026.09

  • Lance 智驾实践 | 卓驭:百 PB 级智驾数据存储架构演进 - Weixin/LanceDB 2026.09.16

  • 卓驭起步于 2016 年,致力于成为国际一流的移动物理 AI 公司。依托独有的原生多模态基础模型和生成式 AI 技术,打造可高效适配多场景的统一移动智能技术底座,不仅为车企提供开放、可定制、量产导向的智能辅助驾驶(涵盖 L2 及以上)解决方案,更致力于为不同场景下的自主移动机器人提供底层技术,实现从地面到近地空间的全域智能移动生态赋能。

image

  • 随着研发规模扩大,训练和推理算力逐步走向多数据中心混合云,数据规模和访问需求也持续增长。计算资源可以按需调度,但任务启动仍可能受挂载状态、数据回源和模型加载影响。如何让数据及时就绪、减少算力等待,成为存储架构演进的重要目标。

  • 卓驭基于 JuiceFS 构建统一数据底座(内部迭代版本名为 ZBoost),通过统一访问、多存储池和共享缓存,支撑数据接入、容量扩展与热点复用,再结合主动预热和读取优化,加快数据交付。

目前,线上集群已承载百 PB 级数据,在线读取能力达到百 GB/s 量级。
本章节将围绕架构演进与生产实践展开。

智驾数据闭环:容量持续增长,数据反复消费

  • 智能辅助驾驶的研发围绕【数据闭环】展开:路采车和量产车回传的数据经过筛选、标注,进入模型训练,再经过评测与发布;新问题和新数据又进入下一轮迭代。

在这个过程中,数据在每个环节的就绪速度,直接影响算力是否需要等待,也影响研发效率

  • 存储角度看,【智驾研发】是一条数据密集型链路【多模态数据】持续回流,并被不同研发任务反复消费。

量产车路采车回传的数据,加上仿真产出的数据,首先进入共享的多模态数据存储,再由筛选、标注、模型训练、仿真评测和个人研发环境等【下游任务】使用。

image

智驾研发中的多模态数据流转与消费

  • 随着【数据规模】和【迭代频次】增长,存储需要同时支撑【持续扩容】与【高效读取】,才能及时向【下游任务】交付数据。

原有架构中的访问、运维与扩展耦合

  • 引入 JuiceFS 之前,我们同时使用 S3 接口和文件访问,多种入口并存,存储配置分散在业务侧,挂载【客户端】则由【计算侧】维护。

image

演进前的多种存储入口与分散配置

  • 由此我们归纳出三类核心痛点:

挂载与调度耦合:任务启动依赖挂载客户端就绪,客户端维护延伸到计算侧,增加运维负担,并可能造成算力等待;
存储配置与业务耦合:Bucket、Endpoint、凭证等存储细节进入业务代码,底层资源变化需要业务配合调整;
容量与带宽扩展耦合:容量持续增长,热点数据反复读取,本地缓存难以跨节点复用,容量与读取带宽难以按需独立扩展。

因此,新架构需要简化业务接入和挂载维护,并支持容量与读取带宽独立扩展。

架构升级:基于 JuiceFS 构建统一数据底座

技术选型:以 S3 为核心,JuiceFS 为底座

  • 为兼容存量业务并支撑后续扩展,我们选择 JuiceFS 作为存储底座,主要考虑以下:

• 多协议访问:支持 S3、POSIX 等接口,兼容不同业务的接入方式。
• 容量扩展:底层依托对象存储,承载持续增长的数据。
• 缓存加速:社区版已有本地缓存能力,可作为后续读取优化的基础。
• 开源生态:社区活跃,便于结合生产需求持续改进。

image

JuiceFS 社区版架构图

统一访问层:应用与存储解耦

  • 新架构中,业务通过内部数据平台的 SDK 访问存储。SDK 提供统一的逻辑命名空间,并处理底层存储位置与访问凭证的映射。通过 SDK 接入的业务以 S3 访问为主,减少计算侧的挂载维护;存量业务仍可使用 POSIX 接口。

image

基于 SDK 的统一访问层与逻辑命名空间

  • 以 S3 为核心的接入方式,也方便我们在入口进行流量隔离、监控和审计。
  • 统一访问层将应用与存储的变化分开,减少底层资源调整对业务接入的影响。在保持上层接口稳定的同时,我们可以继续扩展容量、增加缓存和接入外部数据。

核心能力构建

  • 我们基于 JuiceFS 社区版扩展了多存储池、分布式缓存和 UFS,整体架构如下。

image

多存储池、分布式缓存与 UFS 的整合架构

多存储池:容量扩展与故障隔离

  • 我们在 JuiceFS 单卷的数据层增加路由,根据文件大小、权重等策略,将写入分配到不同存储池。后端可以是多个 S3 Bucket,也可以是文件存储。

image

单卷多存储池的写入路由与容量扩展

  • 这样,JuiceFS 卷与 Bucket 形成一对多关系。当某个对象存储资源达到扩展边界时,可以增加存储池承接后续写入,已有数据不必整体搬迁。多个独立存储池也有助于缩小单一资源域故障的影响范围。

分布式缓存:跨节点复用热点数据

  • 初期我们使用本地缓存,节点之间无法复用缓存数据。任务调度到新节点后,如果本地没有所需数据,就需要再次回源,并在该节点保留一份副本。这既增加读取时延,也降低了缓存空间的利用率。
  • 为此,我们基于社区版开发了分布式缓存能力,多个客户端可以复用共享缓存中的热点数据,对象存储始终作为权威数据源,读取流程如下。

• 客户端通过一致性哈希选择目标缓存节点。
• 缓存节点本地盘命中时,直接返回数据。
• 未命中且允许回源时,由该节点从 S3 读取数据,返回后在缓存节点保留一份。

  • 客户端本地缓存仍然可用,与分布式缓存形成多级缓存架构。

image

分布式缓存读取机制

UFS(Under File System):外部数据接入与缓存加速

  • 我们还基于社区版开发了 UFS 能力,将外部数据源纳入 JuiceFS 的命名空间。UFS 负责外部数据的统一访问和缓存加速,权威副本继续保留在原系统中。

例如,已有一批数据存放在 S3 中,希望使用 JuiceFS 的缓存加速,就可以将其映射到 JuiceFS 的某个路径下。原始数据无需迁移,业务通过映射后的路径访问即可。

image

通过 UFS 将外部数据源映射到统一命名空间

  • 当前映射采用只读方式,支持的后端存储类型与社区版已有的后端支持保持一致。元数据可以按需加载,也可以提前预热;即使没有预先加载,访问目录时也能按需获取外部存储的目录和文件信息。

百 PB 级集群生产实践与优化:缓存编排 / 推理场景 / 训练场景 / Lance 列存储

  • 我们的线上集群已承载百 PB 级数据,带宽达到百 GB/s 量级,缓存容量达到 PB 级。围绕数据准备和任务读取,我们分别优化了缓存编排、模型配送、网络传输和数据格式。

缓存编排:随回传构建与主动预热

  • 我们通过 SDK 统计发现,约八成数据会在回传后的八天内被使用。

因此,我们在数据回传时异步构建分布式缓存,并根据访问规律规划缓存容量和 TTL,使业务读取尽量命中缓存。缓存到期或容量不足时,再进行回收。

image

热点窗口与 TTL 驱动的缓存生命周期

  • 对于需要显式控制数据准备过程的任务,我们引入 Fluid 做主动编排。Dataset 描述数据集及其来源,应用可以通过对应的 Kubernetes PVC 访问数据。
  • 在任务流程中,DataLoad 配合 JuiceFS Runtime 完成预热,数据 Ready 后业务再开始消费。Fluid Controller 协调资源与状态,管理从缓存构建、业务消费到缓存回收的过程。对于未接入 Fluid 编排的业务,我们也提供 API,由业务自行控制缓存预热和淘汰。

image

Fluid 缓存预热与生命周期编排

推理场景:大规模模型配送

在推理场景中,数据准备主要体现为模型配送:推理 Pod 需要完成模型加载后才能提供服务。之前,Pod 启动时直接从模型仓库加载模型,节点增加后,仓库需要承担大量重复读取。为此,我们将缓存放在模型仓库与推理节点之间。

引入缓存后,模型先预热到分布式缓存,再通过缓存网络分发到各推理节点的本地缓存。同一数据块的请求通过一致性哈希落到同一缓存节点,可以在这里进行 I/O 聚合,减少重复回源。

多个推理 Pod 加载同一模型时,可以通过共享缓存聚合读取请求,将模型仓库的回源数据量尽量控制在一份模型的数据量附近。在内部推理场景中,这套方案使部署速度较原有方式提升了十倍以上。

image

基于共享缓存的推理模型配送

训练场景:TCP 零拷贝优化

数据进入缓存后,任务仍需要通过网络持续读取。为了减少这一过程中的开销,我们针对客户端与缓存节点之间的数据链路进行了优化,先从适用范围较广的 TCP 网络入手。

普通读取路径需要通过 ReadAt 将数据从内核读到用户态缓冲区,再通过 conn.Write 写回内核发送。在服务端命中 cacheFile、且请求范围有效时,可以通过 io.CopyN → sendfile(2) 将文件数据直接送入 TCP Socket,省去这段用户态中转。

image

缓存命中后的 TCP 零拷贝发送路径

  • 为评估优化后缓存链路的整体性能,我们进行了 1M 顺序读压测。多服务端总带宽为 1200 Gbit/s,实测随着客户端负载增加,吞吐达到约 135–138 GiB/s。吞吐在约 135 GiB/s 时,1M 请求的 P99 时延约为 2 ms;继续提高到约 138 GiB/s 时,P99 时延升至约 20–30 ms。这说明接近带宽上限时,仍需在吞吐与尾延迟之间取舍。

image

顺序读压测

  • 在 MLPerf Storage UNet3D 吞吐型负载中,我们首先采用单客户端高密度拓扑,将 8 个 accelerator rank 的并发读取压力集中到单节点、单条 200 Gbit/s 数据入口,验证单客户端的数据供给上限。测试覆盖 H20、H100 和 B200 三档负载;其中 H100 和 B200 已触及 200G 网络及客户端读取链路上限,瓶颈由后端存储转移至客户端数据入口。下一阶段将把 UNet3D 训练负载扩展至多客户端,并结合 RDMA,进一步验证大规模多机多卡训练下的持续供数能力与加速卡利用率。

image

MLPerf UNet3D 理论带宽需求与实测结果

Lance 列存储:从数据格式层面减少 I/O

  • 除了提高存储链路的吞吐,我们也通过调整数据格式,减少业务实际需要读取的数据量和 I/O 次数。
  • 以标注数据为例,同一个资产可能对应多个 JSON 文件,之前会将它们打成 tar 包存储。下游处理时仍需要逐帧解析 JSON,进行重复序列化和格式转换,产生大量小 I/O。
  • 我们将标注数据切换为 Lance,把下游常用的 JSON 字段展开为列。数据通过 Arrow Batch 在内存中传递,批量写入 Lance Table,下游按需选择列并批量读取,减少无关字段读取、逐帧 JSON 中转和重复序列化。

image

  • 不同流水线的收益有所差异:I/O 耗时下降约 20%–90%,IOPS 需求下降约 90%,带宽消耗下降 60% 以上,大幅降低了业务负载对存储资源的需求。

Lance 与 JuiceFS 结合预热

  • 按列读取也为预热提供了更精确的范围。我们通过 Lance Manifest 等布局元信息定位所选列的物理字节区间,再映射到相交的 JuiceFS 数据块,只预热相关块,减少无关数据读取。

image

Lance 列到 JuiceFS 预热数据块的映射

  • 相关能力已合并至 JuiceFS 社区版:按字节范围预热(PR #7398[1]),以及独立的 Lance 数据集解析工具(PR #7399[2])。两者配合,可根据指定的数据集版本和列,生成文件及字节范围列表,用于按需预热。

总结

  • 回顾这次实践,我们以 JuiceFS 为统一数据底座,沿三条路径逐步演进:
  • 稳定入口:通过统一访问层屏蔽底层存储差异,使存储扩容和部署调整尽量不影响业务接入。
  • 共享热点:通过分布式缓存跨节点复用数据,减少任务迁移、扩容和多副本读取中的重复回源。
  • 主动就绪:通过 Fluid 编排和 API 控制,让数据在业务消费前完成准备,以数据就绪状态衔接任务运行。

这三条路径共同支撑了百 PB 级集群上的数据交付,百 GB 级以上的性能需求,也降低了存储运维与业务协同成本。目前,我们仍在持续优化缓存编排、网络传输和数据格式,让数据更快就绪、减少算力等待,为智驾研发提供更稳定的存储底座。

推荐文献

CASE 从 CephFS 到 JuiceFS:同程旅行亿级文件存储平台构建之路 | 2024.12

Z FAQ for JuiceFS

Q: JuiceFS 解决了 MinIO 这些对象存储的什么核心问题?(必读)*

两者定位不同,本质上是互补关系而非替代。

JuiceFS 解决的是"对象存储(含 MinIO)不能当文件系统用"这一核心痛点。

  • 核心关系:MinIO 提供对象存储底层 → JuiceFS 在其之上构建 POSIX 文件系统语义层

  • JuiceFS 解决的 MinIO 核心问题:

MinIO 的核心问题 JuiceFS 的解决方式
无 POSIX 文件系统语义 — MinIO 只有 S3 API(Key-Value 语义),不能 mount、不能随机写无目录树无文件锁传统应用程序无法直接使用 提供完全 POSIX 兼容的 FUSE 挂载,通过 8813 项 pjdfstest 测试,现有应用零修改即可使用
无强一致性共享读写 — S3 是最终一致性,多客户端同时读写同一数据行为不可预测 提供 close-to-open 强一致性,支持数千客户端同时读写,修改在所有节点立即可见
无文件锁 — 对象存储不存在 flock/fcntl 语义,并发写入同一文件无法协调 支持 BSD locks (flock) 和 POSIX record locks (fcntl)
无目录树结构 — 只有 flat bucket/key 命名空间,ls/rename/mkdir 等操作语义不完整 在元数据引擎中维护完整的 inode 目录树,支持原子 renamemkdirstat
元数据查询能力弱 — 对象的"元数据"只有 Key+自定义标签,无法做高效的目录遍历、文件属性查询 独立元数据引擎(Redis/MySQL/TiKV),元数据访问延迟 1-3ms,远低于对象存储的 20-100ms
大文件随机写性能差 — S3 不支持覆盖写,修改文件需重新上传整个对象 采用 Chunk/Slice/Block 追加写机制,随机写只上传新 Block + 更新元数据,无需读-改-写整个对象
无多级缓存 — 每次读取都需访问对象存储,延迟高 内置元数据缓存 + 本地数据缓存 + 预读多级缓存,预热后接近本地文件系统性能
无 Hadoop/HDFS 兼容 — 大数据生态无法直接使用 S3 作为 HDFS 替代 提供 Hadoop Java SDK,完整兼容 HDFS 语义,可直接替代
无配额/回收站/ACL — 纯对象存储缺少企业级文件管理能力 支持目录级配额回收站POSIX ACLApache Ranger 集成
协议单一 — 仅 S3 API 一种接入方式 支持 FUSE、Hadoop SDK、K8s CSI、S3 Gateway、WebDAV、Python SDK 等多协议接入

总结:MinIO 解决了"【存】"的问题(廉价海量对象存储),JuiceFS 解决了"【用】"的问题(让对象存储具备完整文件系统语义、强一致性、多协议接入和高性能缓存)。两者常组合使用——MinIO 作为 JuiceFS 的数据存储后端。

image

Q: JuiceFS vs. Iceberg / Paimon(湖仓格式)?(必读)

  • 两者完全不在同一层,不是竞品,而是互补关系。

本质区别

维度 JuiceFS Iceberg / Paimon
本质层 存储基础设施 — POSIX 文件系统 数据湖表格格式(Table Format) — 在文件之上的元数据管理层
提供什么 目录树、文件读写、POSIX 语义、文件锁 表 schema、分区、ACID 事务、时间旅行、快照
数据粒度 无结构的字节流(文件) 结构化的行列数据(Parquet/ORC 文件 + 元数据)
一致性模型 close-to-open 文件级一致性 表级 ACID 事务(MVCC + 快照隔离)
核心能力 mount、读写文件、缓存、配额、回收站 schema 演进、分区演进、upsert/merge、增量读取
使用者 应用程序、容器、Hadoop、AI 框架 Spark、Flink、Trino、StarRocks 等计算引擎
元数据存储 Redis/MySQL/TiKV(文件元数据) Hive Metastore / 文件系统目录(表元数据)
开源协议 Apache 2.0 Apache 2.0

一句话:JuiceFS 管的是"文件怎么存",Iceberg/Paimon 管的是"表怎么管"。前者是底层存储,后者是建在文件之上的表格式层。

可以结合使用吗?——可以,且天然互补

典型架构:

计算引擎 (Spark / Flink / Trino)
        │
        ├── 读写表元数据 ──→ Hive Metastore(表 schema、分区信息)
        │
        └── 读写数据文件 ──→ JuiceFS(挂载为目录,FUSE/CSI)
                                │
                                ├── 数据层 → 对象存储 (S3/MinIO/OSS)
                                └── 元数据层 → Redis/MySQL/TiKV

具体做法

Iceberg/Paimon 的表数据文件(Parquet/ORC)和元数据文件本身就是普通文件,存储路径只需指向 JuiceFS 挂载点即可:

-- Spark 中创建 Iceberg 表,location 指向 JuiceFS 挂载目录
CREATE TABLE iceberg_catalog.my_db.my_table (
  id BIGINT,
  data STRING
) USING iceberg
LOCATION '/mnt/juicefs/warehouse/my_table';

-- Paimon 同理
CREATE TABLE paimon_catalog.my_db.my_table (
  pk INT,
  v STRING,
  PRIMARY KEY (pk) NOT ENFORCED
) WITH (
  'path' = '/mnt/juicefs/warehouse/my_table'
);

结合后的收益

痛点 单独用 Iceberg/Paimon 结合 JuiceFS
小文件多 Iceberg/Paimon 产生大量小文件,对象存储 listing 慢 JuiceFS 元数据引擎做目录索引,listing 性能远超直接访问对象存储
元数据延迟 直接读写对象存储,每次 list 目录延迟 20-100ms JuiceFS 元数据缓存 + 数据缓存,元数据延迟降至 1-3ms
计算引擎访问 需要 S3A/Hadoop 配置,性能受限于对象存储 FUSE 挂载即用,计算引擎透明访问,性能接近本地
多引擎共享 各引擎各自连 S3,无共享缓存 JuiceFS 多客户端共享缓存,数据预热一次多引擎受益
K8s 场景 需配置 S3A,容器无本地缓存 CSI Driver 直接挂载 PV,容器内即为本地文件
非计算引擎访问 只能通过 SQL 引擎,无法直接 cat/cp FUSE 挂载后可直接用 shell 命令查看表数据文件
数据迁移/同步 手动操作对象存储 juicefs sync 一键迁移,支持断点续传

注意事项

  • 不能让 JuiceFS 和 Iceberg/Paimon 独立管理同一批文件:表格式层的元数据(manifest、snapshot)与文件系统层的元数据是不同维度的,各管各的,不会冲突
  • JuiceFS 不提供表级 ACID:这是 Iceberg/Paimon 的职责,JuiceFS 只提供文件级一致性
  • compaction 不冲突:Iceberg/Paimon 的表级 compaction(合并小文件)会产生新文件,JuiceFS 的 Slice compaction(合并数据碎片)是更底层的物理操作,两者独立运行

总结:JuiceFS 是 Iceberg/Paimon 的加速器和便捷访问层,三者叠加 = 对象存储成本 + 文件系统性能 + 表格式 ACID 能力。

Q: 怎么升级 JuiceFS 客户端?

首先卸载(umount)JuiceFS 文件系统,然后使用新版本的客户端重新挂载。建议先在测试环境验证兼容性后再升级生产环境。

Q: JuiceFS 的日志在哪里?

不同类型的客户端获取日志方式不同。FUSE 挂载的日志默认在 $HOME/.juicefs/juicefs.log(root 用户在 /var/log/juicefs.log)。也可以通过 --log=path 指定日志路径。

Q: JuiceFS 是否可以直接读取对象存储中已有的文件?

不可以。JuiceFS 有自己的数据组织格式(Chunk/Slice/Block),不是一般意义上的对象存储访问工具。如需迁移对象存储中的已有数据,请使用 juicefs sync 命令。

Q: 为什么删除文件后对象存储占用空间没有变化?

两个原因:

  1. 回收站:默认开启,删除的文件被放入回收站而非真正删除。可通过 juicefs config --trash-days 0 关闭,或等待保留期过后自动清理。
  2. 异步删除:JuiceFS 是异步删除对象存储中的数据,空间变化会有延迟。可运行 juicefs gc --delete 手动触发清理。

Q: JuiceFS 的性能如何?

  • 元数据访问延迟:1-3 毫秒(取决于网络往返)
  • 数据访问延迟:20-100 毫秒(取决于对象存储)
  • 顺序读写吞吐量:50 MiB/s 至 2800 MiB/s(取决于网络带宽和压缩比)
  • 缓存预热后,访问延迟和吞吐量接近单机文件系统性能

Q: JuiceFS 支持随机读写吗?

支持,包括通过 mmap 等进行的随机读写。随机写时,要覆盖的数据块的元数据被标记为旧数据,新数据块上传到对象存储并更新元数据。读取时根据最新元数据从新数据块读取。建议如需更好的随机读性能,关闭压缩(--compress none)。

Q: 怎么快速拷贝大量小文件到 JuiceFS?

挂载时加上 --writeback 选项,数据先写入本地缓存再异步上传,比直接上传快很多倍。

Q: JuiceFS 支持分布式缓存吗?

企业版支持分布式缓存。社区版仅支持单节点本地缓存。

Q: JuiceFS 目前有哪些 SDK?

  • Hadoop Java SDK(官方维护):高度兼容 HDFS 接口
  • Python SDK(社区维护 → v1.3 起官方支持):原生实现 fsspec,便于接入 Ray 等框架

Q: 一个文件系统可以绑定多个不同的对象存储吗?

不支持。但可以在创建时关联同一个对象存储的多个 bucket(通过 --shards 选项),解决单个 bucket 对象数量限制问题。

Q: JuiceFS 支持使用对象存储中的某个目录作为 --bucket 的值吗?

到 v1.0 为止不支持该功能。

Q: 为什么同名用户在不同主机上权限不同?

虽然用户名相同,但各主机上的 UID/GID 可能不同。使用 id 命令查看具体 UID/GID,并参考「多主机间同步账户」文档解决。

Q: JuiceFS 的异步删除流程是怎样的?

  1. 未开启回收站:文件被检查是否正在使用 → 是则标记 sustained(等待关闭)→ 否则标记 delfile 并放入删除队列
  2. 开启回收站:文件移动到回收站目录(按小时创建子目录)→ 后台任务根据保留天数清理过期文件 → 标记 delfile 并放入删除队列
  3. 删除队列处理:查找文件所有 Chunk 并删除 → 减少 Slice 引用计数 → refs 降为 0 的 Slice 成为 Pending Deleted → 后台清理对象存储中的数据片段

Q: 为什么文件系统数据量与对象存储占用空间存在差异?

  • 随机写产生碎片,对象存储占用 ≥ 实际大小(可通过 juicefs gc --compact --delete 清理)
  • 回收站保留的文件未真正删除
  • 碎片合并后旧碎片仍在回收站中保留
  • 开启压缩后对象存储占用可能 < 实际大小
  • 对象存储可能有最小计量单位(如 OSS 低频存储最小 64KB)

Y 推荐文献

X 参考文献

posted @ 2026-09-16 13:13  千千寰宇  阅读(25)  评论(0)    收藏  举报