<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>JuiceFS Blog</title><link>https://www.juicefs.com/zh-cn/blog/</link><description>Latest news from JuiceFS</description><atom:link href="http://juicefs.com/zh-cn/blog/latest/feed/" rel="self"/><language>zh-cn</language><lastBuildDate>Wed, 28 Oct 2026 08:10:00 +0000</lastBuildDate><item><title>HeyGen：如何让全球 10+ GPU 集群训练不再等待数据？</title><link>https://www.juicefs.com/zh-cn/blog/user-stories/heygen-global-gpu-clusters-ai-data-supply</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;HeyGen 是一家 AI 视频生成与数字人平台，用户超过 3,000 万，覆盖 196 个国家，并被 85% 的 Fortune 100 企业采用。&lt;strong&gt;其研发、训练和推理任务分布在全球 10 多个 GPU 集群中。随着业务规模增长，如何在不同集群间实现高效的数据共享，成为其存储架构建设中亟待解决的关键问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本文分享 HeyGen 团队基于 JuiceFS 企业版的存储实践：通过统一文件命名空间和多级缓存预热，优化推理业务中模型与依赖的分发和加载；同时利用 RDMA 的高带宽传输，结合数据集批次预加载，减少训练过程中的 GPU 等待。&lt;/p&gt;
&lt;p&gt;该架构已在生产环境运行一年以上，支撑约 6 PB 数据、约 10 亿个 inode 和 1,000–2,000 个客户端节点。其中，100 GB 模型冷读取时间从 20–30 分钟缩短至 60–120 秒。&lt;/p&gt;
&lt;h2&gt;01 全球 10+ GPU 集群数据访问挑战&lt;/h2&gt;
&lt;p&gt;HeyGen 的计算资源来自不同数据中心和 GPU 供应商，任务会根据可用算力在不同集群间动态调度，所需的数据集、模型和依赖未必与计算资源位于同一区域。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果每个集群都独立维护一份数据副本，就需要在集群间复制或同步数据，不仅耗时，还可能产生额外的网络出口费用。不同集群的数据副本也可能出现版本差异，增加同步和一致性维护的工作量。当算力资源发生变化时，数据还需要随之迁移，进一步增加任务启动和运维的复杂度。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因此，HeyGen 需要一个能够跨云、跨区域提供统一命名空间的文件系统，让不同集群通过统一路径访问数据，而不必由用户手动管理数据副本的流转。&lt;/p&gt;
&lt;h2&gt;02 存储方案：统一命名空间、跨集群共享与 POSIX 兼容&lt;/h2&gt;
&lt;p&gt;在选型过程中，HeyGen 考虑过云上和机房 NAS，以及对象存储加数据复制等方案。云上或机房 NAS 虽然能提供文件系统接口，但通常面向单一站点部署，吞吐能力也受存储设备限制。对象存储加数据复制虽然能够提供较高带宽，但跨区域访问延迟、数据复制和同步仍需额外处理。此外，HeyGen 的研发团队主要通过 POSIX 接口访问数据，如果改用对象存储接口，就需要改造现有代码，增加迁移成本。&lt;/p&gt;
&lt;p&gt;综合这些因素，HeyGen 最终选择 JuiceFS 企业版：以对象存储作为数据底座，不需要团队自行维护存储节点，减少存储侧的运维负担；同时通过统一命名空间、POSIX 接口和集群侧缓存，满足跨集群数据共享与访问性能需求。&lt;/p&gt;
&lt;p&gt;整体架构可以分为四层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问层：R&amp;amp;D、Training、Inference 等工作负载通过 POSIX 挂载访问文件。  &lt;/li&gt;
&lt;li&gt;缓存层：客户端本地缓存与集群内的分布式缓存共同承担数据读取。  &lt;/li&gt;
&lt;li&gt;元数据层：使用 JuiceFS 企业版管理文件系统元数据。  &lt;/li&gt;
&lt;li&gt;数据层：使用跨区域、跨云的对象存储保存文件数据。&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;HeyGen 的整体存储架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 1｜HeyGen 的整体存储架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;跨集群共享：从独立副本到统一访问&lt;/h3&gt;
&lt;p&gt;在原有模式下，不同集群可能使用不同版本的数据，例如一个集群使用 data v3，另一个仍然使用 data v2。团队需要额外处理数据复制、版本更新和同步。&lt;strong&gt;采用 JuiceFS 后，HeyGen 将多个集群接入同一个全局文件系统，通过统一挂载入口访问数据，并利用各集群的 Cache Group 加速读取，从而减少重复复制和版本同步的工作。&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;共享文件系统&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 2｜从集群独立副本到全局共享文件系统&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;保持 POSIX 访问方式&lt;/h3&gt;
&lt;p&gt;除了跨集群共享，HeyGen 还希望尽量减少存储变化对应用代码的影响。研发和数据处理代码原本主要通过 POSIX 文件路径访问数据，而不是直接调用对象存储接口。如果改用对象存储访问，就需要额外进行代码迁移和适配。&lt;/p&gt;
&lt;p&gt;JuiceFS 保留了文件系统接口，应用仍然可以通过普通路径访问文件，例如：&lt;code&gt;open('/jfs/data/...')&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;因此，研发人员可以继续沿用原有的文件访问方式，也更容易保持本地开发与线上运行的一致性。&lt;strong&gt;对 HeyGen 来说，POSIX 兼容不仅降低了应用迁移成本，也减少了业务代码对底层存储变化的感知。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;03 多级缓存架构&lt;/h2&gt;
&lt;p&gt;统一文件系统解决了不同集群访问同一份数据的问题，但如果任务仍然直接从跨区域、跨云的对象存储读取模型和训练数据，加载速度仍会受到网络距离、传输带宽和并发读取压力的影响。尤其当大量任务同时启动、重复读取相同文件时，数据加载可能拖慢任务启动，甚至让 GPU 因等待数据而闲置。&lt;/p&gt;
&lt;p&gt;为此，HeyGen 采用多级缓存架构，将高频访问的数据缓存在更靠近计算资源的位置，减少重复从远端存储读取数据的等待。整体数据访问链路包括以下四个层次：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内存&lt;/strong&gt;：每个节点的读写缓冲和元数据缓存。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地缓存&lt;/strong&gt;：客户端将读取的数据缓存到节点上的 NVMe 磁盘。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分布式缓存&lt;/strong&gt;：每个集群部署独立的 Cache Group，在集群内共享缓存数据。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局存储&lt;/strong&gt;：跨区域、跨云的对象存储，保存完整数据，作为共享的数据底座。&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;JuiceFS 多级缓存架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 3｜JuiceFS 多级缓存架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;客户端内存中包含读写 Buffer 和元数据缓存。其中，读写 Buffer 是分配给客户端进程的内存，其大小由 &lt;code&gt;--buffer-size&lt;/code&gt; 控制，读写操作都会经过这块缓冲区，因此该配置会影响客户端的预读性能和读写并发；元数据缓存则可以减少客户端与元数据服务之间频繁的列表（ls）和查询操作，从而提升元数据访问性能。同时，元数据缓存支持主动失效，以确保强一致性。&lt;/p&gt;
&lt;p&gt;在磁盘缓存层，本地缓存服务于单个客户端；而 Cache Group 则将同一集群内多个节点上可用于缓存的 NVMe 组织成分布式缓存，供集群内的 GPU 工作负载共享读取。HeyGen 为每个集群部署独立的 Cache Group，而所有集群仍通过同一个全局 JuiceFS 文件系统访问完整数据。&lt;/p&gt;
&lt;p&gt;在这套架构中，如何在有限的缓存空间内优先保留高频访问的数据，是提升缓存利用效率的关键。&lt;strong&gt;JuiceFS Cache Group 支持按目录或文件设置缓存优先级；缓存空间不足时，优先级越高的数据越晚被淘汰。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;除了通过缓存优先级控制数据的淘汰顺序，HeyGen 还会使用 warmup，将已知任务即将使用的数据提前加载到 Cache Group，减少任务启动后的首次读取等待。&lt;/p&gt;
&lt;p&gt;接下来，我们以模型与 Python 依赖分发、训练数据加载为例，介绍 HeyGen 如何根据不同工作负载运用这套多级缓存架构。&lt;/p&gt;
&lt;h2&gt;04 实践一：模型与 Python 依赖分发&lt;/h2&gt;
&lt;p&gt;训练或推理任务启动时，在读取数据集之前通常需要加载模型和安装 Python 依赖。当大量任务实例同时启动时，如果每个实例都分别从上游存储或 Python 镜像源获取这些文件，启动速度就会受到网络条件和上游服务性能的影响。&lt;/p&gt;
&lt;p&gt;HeyGen 希望在任务从单机扩展到数千个节点时，仍尽可能保持原有的开发体验。实际的数据处理需求变化较快，一天可能需要同步数十个版本；如果每次变化都重新构建和分发 OCI 镜像，不仅会增加开发流程复杂度，也会增加使用者的心智负担。因此，HeyGen 没有把所有依赖变化都转化为 OCI 镜像，而是继续保留原有的 Python 依赖安装方式。&lt;/p&gt;
&lt;p&gt;基础设施需要解决的问题也随之变成：在尽量不改变现有开发方式的前提下，如何让不同集群中的任务快速获取相同的模型和 Python 包。&lt;/p&gt;
&lt;h3&gt;模型缓存与预热&lt;/h3&gt;
&lt;p&gt;模型文件通常体积较大，任务启动时需要加载，也可能被多个任务重复使用。为了减少首次读取带来的等待，HeyGen 会将模型文件设为较高的缓存优先级：&lt;code&gt;/demo/models：缓存优先级 3；&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;同时，团队定期将高优先级模型预热到 Cache Group，让任务启动时优先读取已经准备好的缓存数据。客户端侧还结合 Page Cache 与 Prefetch 优化模型读取链路。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;模型预热的具体配置如下：&lt;/p&gt;
&lt;pre class="codehilite"&gt;&lt;code class="language-bash"&gt;juicefs mount models /mnt/jfs-warmup   
  --cache-group=$CACHE_GROUP   
  --no-sharing   
  --group-network=$NIC   
  --cache-size=0   
  --buffer-size=8192 -d

juicefs warmup 
  /mnt/jfs-warmup/models   
  -c 64 
  --cache-priority=3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;预热任务按小时运行。&lt;strong&gt;其中，&lt;code&gt;--cache-size=0&lt;/code&gt; 使预热进程将数据填充到 Cache Group，而不占用自身的本地磁盘缓存。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Python 依赖分发&lt;/h3&gt;
&lt;p&gt;Python 依赖同样会被不同任务反复使用，但缓存空间有限，团队需要在模型和依赖之间分配缓存资源。因此，Python 依赖的缓存优先级低于模型：&lt;code&gt;/demo/deps：缓存优先级 2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其他数据的缓存优先级则设为 0。这样，当缓存空间不足时，系统会优先保留模型，其次是常用 Python 依赖，其他数据则更早被淘汰。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为了避免每个任务启动时都分别下载依赖，HeyGen 会提前将 Python 包放到 JuiceFS 路径中，并在任务提交时预热所需依赖&lt;/strong&gt;。安装时，任务将索引地址指向该路径，例如：&lt;code&gt;--index-url /jfs/pypi/simple&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;不同集群中的任务可以通过统一的 JuiceFS 路径访问相同依赖。缓存未命中时，节点从上游获取文件，其他工作负载则可以复用集群内已有的缓存，减少重复拉取造成的上游压力，也降低了各集群分别维护外部 Python 镜像源的需求。&lt;/p&gt;
&lt;p&gt;在 1,000+ 并发场景中，HeyGen 对模型和 Python 依赖加载进行了测试，结果如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100 GB 模型冷读取：从约 20–30 分钟缩短至 60–120 秒。  &lt;/li&gt;
&lt;li&gt;uv 依赖安装：从约 5 分钟缩短至约 30 秒。&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;缓存分发机制&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 4｜模型与 Python 依赖的缓存分发机制&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;具体效果会随工作负载和运行环境变化。对 HeyGen 来说，更重要的是，模型和依赖的分发不再完全受各集群外部网络条件影响，高频使用的数据可以提前准备在靠近计算资源的缓存中，从而减少任务启动阶段的等待。&lt;/p&gt;
&lt;h2&gt;05 实践二：训练数据加载与减少 GPU 等待&lt;/h2&gt;
&lt;p&gt;与模型和依赖分发关注任务启动不同，训练场景更关注训练过程中数据能否持续供给。训练数据可能存放在一个区域，而训练任务运行在另一个拥有大规模 GPU 资源的集群中。如果数据加载跟不上训练节奏，GPU 就可能因等待数据而闲置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;针对这一问题，HeyGen 利用训练节点上的 NVMe 构建更大规模的 Cache Group，并结合 RDMA 和批次预热，减少数据读取开销和 GPU 等待时间。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;利用训练节点 NVMe 构建大规模缓存&lt;/h3&gt;
&lt;p&gt;训练节点通常具有较大的本地 NVMe 空间。HeyGen 将这些 NVMe 资源组织为集群级 Cache Group，使训练任务能够优先从集群内缓存读取数据。&lt;/p&gt;
&lt;p&gt;为了提高客户端从 Cache Group 读取数据时的传输效率，团队使用 RDMA 加速客户端与缓存节点之间的数据传输。该能力适用于 JuiceFS Enterprise Edition 5.3 及以上版本，客户端和缓存节点两端都需要配置相应的 rdma-network。在 HeyGen 的实际运行环境中，Cache Group 与 RDMA 配合后，缓存读取基本可以打满网卡带宽。&lt;/p&gt;
&lt;p&gt;除了提高缓存读取速度，HeyGen 还希望尽量让数据加载不阻塞训练过程，因此进一步将下一批数据的预热与当前批次训练重叠。&lt;/p&gt;
&lt;h3&gt;提前加载下一批训练数据&lt;/h3&gt;
&lt;p&gt;HeyGen 的训练数据按批次使用。&lt;strong&gt;当训练任务正在使用第 N 批数据时，预热任务会利用暂时空闲的带宽，提前将第 N+1 批数据加载到 Cache Group：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;训练任务读取并处理第 N 批数据。  &lt;/li&gt;
&lt;li&gt;同时，预热任务提前加载第 N+1 批数据。  &lt;/li&gt;
&lt;li&gt;第 N 批训练结束时，第 N+1 批数据已经在缓存中准备就绪。  &lt;/li&gt;
&lt;li&gt;训练继续进行，同时再预热后续批次的数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;这样，数据加载可以与当前批次训练并行进行，减少训练等待，并降低因数据 I/O 造成的 GPU 空闲时间。此外，已完成训练任务的数据会被新加载的数据自动淘汰，从而更有效地利用有限的缓存空间。&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;RDMA 与批次预热如何配合训练&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 5｜RDMA 与批次预热如何配合训练&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;06 小结&lt;/h2&gt;
&lt;p&gt;总体来看，HeyGen 通过统一命名空间、POSIX 兼容和分层缓存，解决了跨云多集群环境下的数据共享、应用迁移和负载冷启动效率问题。整体收益主要体现在三个方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;启动与训练等待减少：模型和 Python 依赖可以提前预热，训练场景可以提前加载下一批数据，减少冷读取和 GPU 等待。  &lt;/li&gt;
&lt;li&gt;数据管理与运维简化：多个集群通过统一命名空间访问数据，减少数据复制、版本同步和多集群运维。  &lt;/li&gt;
&lt;li&gt;存储成本更可控：以对象存储为数据底座，通过多级缓存承接高频访问，兼顾存储成本和访问性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对多云、多 GPU 集群的 AI 基础设施而言，将统一命名空间、POSIX 兼容与分层缓存结合起来，有助于兼顾跨集群数据共享、应用兼容与数据访问效率。欢迎存储与 AI Infra 领域的同行交流，分享在数据共享、模型分发和训练数据加载方面的实践与经验。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Wed, 28 Oct 2026 08:10:00 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/user-stories/heygen-global-gpu-clusters-ai-data-supply</guid></item><item><title>面向 EB 级 AI 存储：JuiceFS 企业版架构演进</title><link>https://www.juicefs.com/zh-cn/blog/engineering/juicefs-enterprise-architecture-eb-scale-ai-storage</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;当 AI 业务的数据规模从 PB 走向 EB，文件系统面对的就不再是单一的容量问题。文件数增长到千亿级，客户端从几千个扩展到几十万甚至百万个，训练吞吐持续提高，数据回收、跨云访问、多租户隔离和日常运维等压力也随之放大。这些压力最终落在元数据引擎、数据生命周期、数据路径以及客户端与控制面四条技术主线上。&lt;/p&gt;
&lt;p&gt;上一篇介绍了&lt;a href="https://juicefs.com/zh-cn/blog/user-stories/horizon-eb-scale-storage-practice"&gt;地平线 AIDI 基于 JuiceFS 社区版的 EB 级实践&lt;/a&gt;。本文聚焦 JuiceFS 企业版，介绍这四条技术主线如何在 EB 级数据、千亿文件和百万级客户端的规模压力下持续演进。&lt;/p&gt;
&lt;h2&gt;1.元数据引擎：从单节点能力到千亿文件水平扩展&lt;/h2&gt;
&lt;h3&gt;1.1 千亿文件规模下的元数据压力&lt;/h3&gt;
&lt;p&gt;文件数达到千亿级后，元数据访问会暴露三个问题。一是&lt;strong&gt;热点集中&lt;/strong&gt;：训练高峰期，少量热点目录与高频操作会让单个元数据节点成为瓶颈。二是&lt;strong&gt;延迟放大&lt;/strong&gt;：路径解析、属性查询、文件打开都要访问元数据，任何额外往返都会直接拖慢训练任务。三是&lt;strong&gt;规模压力&lt;/strong&gt;：回收站等大目录可达数百亿个目录项，挂载配置、容量信息等全局数据被反复查询。&lt;/p&gt;
&lt;p&gt;面向这类规模，企业版从元数据引擎本身入手，通过自研元数据服务打散热点、降低访问延迟，并支持水平扩展。&lt;/p&gt;
&lt;h3&gt;1.2 元数据引擎设计：全内存、无锁与多分区&lt;/h3&gt;
&lt;p&gt;企业版元数据引擎采用&lt;strong&gt;全内存 + 单线程无锁 + Raft&lt;/strong&gt; 的设计。所有核心数据结构在单线程中处理，天然保证操作原子性，避免多线程锁竞争带来的延迟尖刺；全内存让请求不必回落磁盘，单请求处理时间维持在百微秒量级；Raft 负责多机复制与自动故障切换。在 30 GiB 内存下，单个元数据进程可以管理约 3 亿个文件。&lt;/p&gt;
&lt;p&gt;但全内存也意味着内存用量直接决定硬件成本。在千亿文件规模下，单文件内存占用的微小差异会被放大成巨大的资源开销。通过内存池、手动内存管理、空闲目录序列化压缩、小文件紧凑格式等手段，企业版把文件元数据平均内存占用从最初近 600 字节降至约 100 字节——约为 HDFS NameNode 的 27%、CephFS MDS 的 3.7%。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;系统&lt;/th&gt;
&lt;th&gt;单文件元数据内存占用&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HDFS NameNode&lt;/td&gt;
&lt;td&gt;约 370 B&lt;/td&gt;
&lt;td&gt;第三方技术博客&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CephFS MDS&lt;/td&gt;
&lt;td&gt;约 2,700 B&lt;/td&gt;
&lt;td&gt;Ceph 集群监控（32 GB 内存 / 1,200 万文件）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alluxio（堆内存模式）&lt;/td&gt;
&lt;td&gt;约 2,100 B&lt;/td&gt;
&lt;td&gt;Alluxio 文档：64 GB 内存约 3,000 万文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JuiceFS 社区版&lt;/td&gt;
&lt;td&gt;约 430 B&lt;/td&gt;
&lt;td&gt;社区版文档&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JuiceFS 企业版&lt;/td&gt;
&lt;td&gt;约 100 B&lt;/td&gt;
&lt;td&gt;生产集群监控（30 GB 内存 / 3 亿文件）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;更高的内存效率意味着：同样的硬件可以承载更多文件，换句话说，千亿文件规模所需的元数据成本被显著压低。&lt;/p&gt;
&lt;p&gt;在单进程之上，企业版通过&lt;strong&gt;多分区（Zone）&lt;/strong&gt; 水平扩展：每个分区负责文件树的一部分，由客户端组装成单一命名空间；子树尽量保持完整，让绝大部分操作落在单一分区内，减少分布式事务；跨分区迁移通过链接与引用完成，对 POSIX 路径透明；超大目录还可以拆分为多个虚拟子目录，获得更细的迁移粒度，避免单个大目录长期压在某个分区上。每个 inode 的高位携带分区信息，客户端自动感知分区拓扑并连接所有分区——新增分区无需修改业务访问方式。&lt;/p&gt;
&lt;p&gt;可靠性方面，每个分区由三个实例组成（服务 / 热备 / 冷备实例），服务与热备实例采用差异化的内存压缩策略，快照与变更日志持续落盘并可异地备份（见 2.2 节）。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多分区元数据架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 1：多分区元数据架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;1.3 版本演进与规模验证&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;v5.2&lt;/strong&gt; 开始，企业版重点优化多分区稳定性和元数据内存占用，文件管理规模进入千亿级。生产环境中，单集群已管理超过 500 亿文件，元数据访问延迟维持在毫秒级。&lt;/p&gt;
&lt;p&gt;到了 &lt;strong&gt;v5.3&lt;/strong&gt;，分区上限提升至 1,024 个，单文件系统可支持超过 5,000 亿个文件。与此同时，系统开始支持热点自动迁移，并提供相应的运维工具：高访问目录可以拆分为更小的单元并发迁移，以更快地分散热点；周期性自检则用于发现并修复跨分区迁移过程中可能遗留的中间状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;v5.4&lt;/strong&gt; 进一步把扩展重点从文件数量延伸到客户端规模。元数据节点与网络代理可合并部署，客户端可以连接任一节点，跨分区请求由集群内部转发；配合连接复用，客户端接入规模提升到百万级，相关设计将在第 4 节展开。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;版本演进——从千亿文件到百万客户端&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 2：版本演进——从千亿文件到百万客户端&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;2.数据生命周期：把删除、恢复与审计内建为系统能力&lt;/h2&gt;
&lt;h3&gt;2.1 PB 级删除下的数据生命周期压力&lt;/h3&gt;
&lt;p&gt;PB 级删除不仅要求空间及时回收，还要兼顾误删恢复、删除追溯和遗漏对象审计。对象存储侧持续产生数十亿待回收对象，一次误删可能影响训练任务，而异常残留对象也需要被持续发现和确认。&lt;/p&gt;
&lt;h3&gt;2.2 回收站、后台任务与审计备份&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;回收站&lt;/strong&gt;是企业版的默认能力：删除先进入回收站，保留期可配置（常见 7 天）；保留期内可通过控制台或恢复命令自助恢复，恢复是元数据操作，不复制数据本体。v5.3 起，多分区环境下的回收站会记录文件的原始路径，保证恢复位置可靠；v5.4 支持按删除时间窗口与关键词筛选，帮助业务在海量删除中定位误删目标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;空间回收&lt;/strong&gt;由统一的「后台任务」体系完成：碎片合并、回收站清理、跨云复制等任务由元数据服务调度到全集群客户端并行执行。这套机制带来三个直接收益：删除能力随客户端数量扩展——要加快回收，增加挂载点即可；速率可控——可按访问令牌限制删除流量、关闭指定客户端的后台任务，碎片合并的执行时间窗则由元数据服务统一配置；执行可观测——碎片合并与删除任务都有监控指标可查。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;审计与容灾&lt;/strong&gt;同样内建：变更日志记录了每一次删除操作及其来源会话，配合审计日志可以定位「谁在什么时间删除了什么」；元数据快照与变更日志由控制台集中配置，默认每小时备份到异地对象存储，本地保留 2 天、建议备份至少 30 天。对象存储侧的遗漏对象由垃圾回收与一致性检查工具负责审计；v5.4 为这两个工具增加外部排序模式，用磁盘分担排序内存，检查亿级文件时内存占用可控（外排模式仅用于检查评估，不执行删除）。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;数据生命周期治理闭环&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 3：数据生命周期治理闭环&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;2.3 数据洞察与数据集分支&lt;/h3&gt;
&lt;p&gt;删除决策需要数据支撑：控制台监控提供回收站文件数、待删除对象分片、碎片等指标；文件查找在元数据侧完成过滤，用于冷热与老化分析；目录 / 用户 / 组配额约束各业务的容量预算。&lt;/p&gt;
&lt;p&gt;为训练准备数据集分支时，v5.4 的&lt;strong&gt;跨分区目录克隆&lt;/strong&gt;在 1 亿文件规模下耗时约 1 分 40 秒，相当于每秒约 100 万个文件。&lt;/p&gt;
&lt;h2&gt;3.数据路径：用缓存、镜像兼顾性能与成本&lt;/h2&gt;
&lt;h3&gt;3.1 EB 级数据访问：吞吐、存储成本与跨云访问&lt;/h3&gt;
&lt;p&gt;训练需要 TB/s 量级的吞吐：数据集加载是大量随机读，训练检查点写入则是大文件顺序写，两类负载对 IOPS 与写吞吐的要求不同；EB 级容量不可能全部放在热层；算力跨云部署还要求数据尽量就近访问。读性能、容量成本与数据位置，必须一起解决。&lt;/p&gt;
&lt;h3&gt;3.2 多级缓存与分布式缓存组&lt;/h3&gt;
&lt;p&gt;企业版把「客户端间共享缓存」做成了产品化的分布式缓存：多个客户端可以组成&lt;strong&gt;缓存组&lt;/strong&gt;，基于一致性哈希共享数据块；节点故障会被自动摘除，未命中的请求穿透到对象存储，读服务不中断。业务客户端也可以配置为仅使用缓存、不对外提供缓存数据，由一组稳定节点组成&lt;strong&gt;专用缓存集群&lt;/strong&gt;，专门服务训练热数据。读取路径同样是分级的：从缓存组取回的数据会继续缓存在客户端本地，重复访问不再经过网络。&lt;/p&gt;
&lt;p&gt;围绕缓存组的运维与效率，企业版提供了一整套工具：数据预热、缓存组备份（提升成员变动期的命中率）、扩容保护（新节点未命中时先向旧节点取数，减少回源）、未命中不回填（保护热点数据）、缓存块合并（降低大缓存盘上的文件数量）、按字节区间精准预热。传输层则通过自研 IORPC 协议（v5.2）与 RDMA（v5.3 起）持续优化。&lt;/p&gt;
&lt;p&gt;这条路径上的量化参考：v5.2 使用 100 台 100Gbps 节点，聚合读带宽达到 1.2 TB/s；v5.3 在 160Gbps 网卡、1MB 随机读场景下，CPU 占用降至 TCP 方案的约 1/3；v5.4 在 400Gbps 客户端和 800Gbps 缓存节点环境下，单客户端读带宽 45 GB/s、单缓存节点 90 GB/s、网卡利用率超过 90%，高并发随机读 IOPS 较此前提升一倍以上。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多级缓存与分布式缓存组&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 4：多级缓存与分布式缓存组&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;3.3 跨地域镜像与按需同步&lt;/h3&gt;
&lt;p&gt;要让数据贴近算力，企业版提供&lt;strong&gt;镜像文件系统&lt;/strong&gt;：在镜像区域部署元数据副本，元数据持续同步；客户端优先读本地对象存储；写入采用读写分离，提交到源集群，保证跨区域一致性。v5.3 起，镜像不可用时读请求可回退到源集群；v5.4 进一步支持按需同步——只持续同步元数据，对象数据在首次访问时按需拉取并缓存在本地，避免为了少量访问同步整个数据集。如果只需要加速元数据读取，还可以直接部署只读元数据节点。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;跨地域镜像与按需同步&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 5：跨地域镜像与按需同步&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;3.4 弹性与存量数据接入&lt;/h3&gt;
&lt;p&gt;数据底座是对象存储：容量按需扩缩，一个文件系统即可统一管理来自不同区域、不同供应商的多个桶，并为所有桶提供缓存能力。对于已经存在于对象存储中的存量数据，可以通过导入功能&lt;strong&gt;透明接入&lt;/strong&gt;——只写元数据、不拷贝数据；v5.2 起支持按需导入（首次访问触发），v5.3 起任意导入对象都能获得缓存加速。热数据由缓存与镜像承载，冷数据留在对象存储，EB 级容量成本不再需要按峰值规划。&lt;/p&gt;
&lt;h2&gt;4.客户端与控制面：支撑百万连接下的隔离、观测与运维&lt;/h2&gt;
&lt;h3&gt;4.1 百万客户端规模下的控制面压力&lt;/h3&gt;
&lt;p&gt;几十万客户端通过 K8s / CSI、S3 网关、SDK 等方式接入，同时活跃连接达到百万级：任何客户端都可能打爆共享的元数据集群；客户端各自产生数百项指标，直抓会带来时序爆炸；多租户的权限与资源预算必须清晰；升级还要尽量不影响训练任务。&lt;/p&gt;
&lt;h3&gt;4.2 连接复用：支撑百万客户端&lt;/h3&gt;
&lt;p&gt;v5.4 把客户端接入能力推进到百万级，核心做法是引入&lt;strong&gt;网络代理层（与元数据节点合并或分开部署均可）&lt;/strong&gt;：客户端可以连接任一元数据节点，本分区请求直接处理，跨分区请求在内部转发；多个客户端会话共用节点间的物理连接。在压测中，10 个内部代理节点承接百万条客户端连接，到元数据服务节点仅需约 400 条后端物理连接。会话管理同步优化：索引定位代替全量遍历，分页查询、采样上报控制状态规模，过期会话分批清理。&lt;/p&gt;
&lt;h3&gt;4.3 多租户隔离、观测与运维&lt;/h3&gt;
&lt;p&gt;在多租户环境下，企业版通过令牌控制客户端的访问边界，包括 IP 范围、读写权限、可访问子目录，以及是否参与后台任务。v5.4 在原有单子目录限制的基础上增加白名单机制，可以为客户端配置多个一级子目录。&lt;/p&gt;
&lt;p&gt;资源控制则覆盖上传、下载、删除和后台任务等不同流量，并支持会话级 CPU 配额，避免单一租户的负载持续挤占共享集群资源。v5.2 还对全局限速消息做了合并，减少限速状态同步本身带来的开销。&lt;/p&gt;
&lt;p&gt;接入层同时保留多种形态，包括 POSIX（FUSE / CSI）、S3 网关、Hadoop SDK、Python SDK 和 Windows 客户端，适配不同训练和数据处理流程。观测侧，控制台集成 Prometheus 与 Grafana，覆盖文件系统、客户端会话和集群健康等指标，也可以接入已有的监控与告警体系。&lt;/p&gt;
&lt;p&gt;在日常运维中，v5.2 起支持通过控制台统一升级客户端并收集诊断信息；元数据服务支持滚动、无缝升级。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;百万客户端接入与多租户治理&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图 6：百万客户端接入与多租户治理&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;5.小结&lt;/h2&gt;
&lt;p&gt;从 v5.2 到 v5.4，JuiceFS 企业版围绕超大规模场景持续演进的重点，并不是增加若干独立功能，而是让元数据、数据生命周期、数据路径和客户端控制面都能够随规模一起扩展。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;元数据侧，通过全内存引擎、多分区和自动均衡，把单节点能力扩展到千亿乃至更大文件规模；&lt;/li&gt;
&lt;li&gt;数据生命周期侧，将回收站、后台任务、审计与备份纳入统一机制；&lt;/li&gt;
&lt;li&gt;数据路径侧，通过分布式缓存、RDMA、镜像和数据导入，让热数据更靠近算力，同时保留对象存储的容量弹性；&lt;/li&gt;
&lt;li&gt;客户端侧，则通过连接复用、访问控制、限速和统一观测支撑几十万到百万级连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些能力想解决的，是规模增长过程中逐步显现的共性问题：让文件系统自身承接更多基础能力，减少平台团队在文件系统之外额外维护的部分。JuiceFS 企业版目前已在 EB 级生产环境中使用，相关设计仍在持续演进。对于正在规划 AI 数据底座的团队，希望这些设计取舍能为正在规划 AI 数据底座的团队提供一些参考。如果有类似场景或挑战，欢迎在评论区交流。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Tue, 20 Oct 2026 13:11:33 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/engineering/juicefs-enterprise-architecture-eb-scale-ai-storage</guid></item><item><title>地平线 EB 级存储工程实践</title><link>https://www.juicefs.com/zh-cn/blog/user-stories/horizon-eb-scale-storage-practice</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;规模决定架构。当容量来到 EB 级、文件数以千亿计、几十万客户端接入，每周有 200PB 数据新增和删除时，开源文件系统的每一个“单点假设”都会被击穿。地平线 AIDI（智能驾驶研发数据平台）同时使用 JuiceFS 社区版与企业版承载 EB 级数据；本文将聚焦 JuiceFS 社区版的实践，分享我们四个工程命题的解决思路、实践效果与工程取舍。&lt;/p&gt;
&lt;h2&gt;从 PB 到 EB，存储面临哪些新问题&lt;/h2&gt;
&lt;p&gt;AIDI 平台支撑地平线智能驾驶研发的数据闭环：路采和量产车回传的数据经筛选、标注进入模型训练，训练产出经评测发布，新问题数据再回流。&lt;strong&gt;存储是这条链路的底座，数据在每个环节的就绪速度，直接决定算力是否空转&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;过去几年，这条链路的规模持续跃升：&lt;strong&gt;数据容量从 PB 级增长到 EB 级，训练算力从千卡规模增长到万卡&lt;/strong&gt;。随着模型迭代加快、数据回流更加密集，存储负载也不断超出原有架构的设计预期。当前的规模和负载表现为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容量：EB 级（10¹⁸ B），且仍在增长，已超出社区版常规测试覆盖的规模；  &lt;/li&gt;
&lt;li&gt;文件数：千亿级（10¹¹），回收站等单个目录可达数百亿 entry；  &lt;/li&gt;
&lt;li&gt;数据周变更：每周约新增 200PB、删除 200PB，对象存储侧持续产生数百亿待删除对象；  &lt;/li&gt;
&lt;li&gt;客户端数量：几十万，通过 Kubernetes（K8s）训练集群、容器存储接口（CSI）、兼容 S3 对象存储协议的网关和软件开发工具包（SDK）直读等方式接入；  &lt;/li&gt;
&lt;li&gt;负载特征：数据集的顺序大 I/O 与训练检查点（checkpoint）的随机小 I/O 并存，冷热分布悬殊，多租户共享同一套元数据（Meta）集群。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由此，我们面对四个工程命题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;千亿文件下，元数据如何避免热点并保持稳定？  &lt;/li&gt;
&lt;li&gt;每周删除约 200PB，如何实现可恢复、可审计的数据治理？  &lt;/li&gt;
&lt;li&gt;EB 级容量下，如何兼顾训练吞吐与存储成本？  &lt;/li&gt;
&lt;li&gt;几十万客户端如何接入、限流和观测？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;自 2021 年起，我们在 AIDI 平台使用 JuiceFS 社区版，并围绕上述命题持续开展定制。随着数据规模和使用场景扩大，我们也逐步引入企业版。目前两种版本均在平台中使用；下文以社区版实践为主线，介绍具体的改造与取舍。&lt;/p&gt;
&lt;h2&gt;命题一：千亿文件元数据优化&lt;/h2&gt;
&lt;h4&gt;缓解元数据热点与大目录压力&lt;/h4&gt;
&lt;p&gt;文件数达到千亿级后，TiKV 元数据访问出现三个瓶颈：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;训练高峰期，主副本（leader）所在节点的处理器（CPU）打满，从副本（follower）仍有余量；  &lt;/li&gt;
&lt;li&gt;事务向集群调度组件 PD（Placement Driver）的时间戳服务 TSO（Timestamp Oracle）申请全局时间戳，增加网络往返；  &lt;/li&gt;
&lt;li&gt;挂载配置、容量信息等热点元数据被反复查询。单纯调参已难以缓解这些压力，需要改造访问路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;针对这些瓶颈，我们改造 TiKV Go 客户端的数据分片（Region）路由缓存，让 follower 分担读请求；减少高频点查的一次 TSO 往返；并以读缓存处理热点元数据。&lt;/p&gt;
&lt;p&gt;相关改动在新旧版本上分别通过持续集成测试验证，并以构建时注入补丁的方式交付，避免长期维护上游依赖的独立分支。此外，缓存集群扩缩容时，我们尽量减少缓存键的大面积重新映射，降低缓存击穿风险。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;元数据访问路径&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;元数据访问路径&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;此外，数十亿 entry 的大目录也会给客户端带来内存压力。我们采用流式按批扫描，避免 readdir 一次性构造完整数组导致内存耗尽，并支持内核 readdir 缓存。&lt;/p&gt;
&lt;h4&gt;限流保障集群稳定&lt;/h4&gt;
&lt;p&gt;几十万客户端共享一套 Meta 集群，任何客户端都能打爆它。&lt;strong&gt;为避免单个租户的流量影响整个集群，我们分别约束元数据和数据面的资源消耗&lt;/strong&gt;：元数据侧基于 PD Resource Group（资源组）令牌桶，以 RU（请求单元）衡量并限制各租户的资源消耗；数据面分别限制缓存集群流量和对象存储回源流量。超出预算的客户端会被限速，避免压力扩散。限流预算采用声明式配置：运维在 YAML 格式的配置文件中维护目标状态，manager 对比当前配置并自动生成数据库建表、修改、删除等 SQL 语句，使配置收敛到目标状态。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多租户限流与资源预算机制&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;多租户限流与资源预算机制&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h4&gt;元数据容灾：冷备兜底&lt;/h4&gt;
&lt;p&gt;元数据是整个系统的命门，千亿文件的归属关系保存在 TiKV 中，如果元数据无法恢复，即使文件内容仍在对象存储中，也难以确定归属。为应对不同风险，我们设置三层保障：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TiKV 的 Raft 多副本应对硬件故障；  &lt;/li&gt;
&lt;li&gt;定期将 TiKV 全量快照备份到异地对象存储，为误删 keyspace 中的元数据、元数据逻辑损坏和地域级故障提供恢复手段；  &lt;/li&gt;
&lt;li&gt;对象存储保留文件内容，使元数据恢复后数据集可以重新使用。需要恢复时，我们重建 TiKV 集群并回放最近一份快照。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;冷备的代价是元数据只能恢复到最近一次备份；此后发生的变更可能丢失，回退范围取决于备份间隔。在当前训练场景下，我们可以接受这一取舍。&lt;/p&gt;
&lt;h2&gt;命题二：数据洞察与数据治理—每周删 200PB 的降本工程&lt;/h2&gt;
&lt;p&gt;每周约 200PB 数据被删除时，逻辑删除只是起点：对象存储侧持续产生数十亿待回收对象，单机扫描难以及时清理；异步删除失败还可能留下难以从对象 key 追溯文件归属的遗漏对象。我们将删除决策、误删恢复、空间回收和泄漏审计串成一条治理链路。&lt;/p&gt;
&lt;h3&gt;删除决策：看板与业务确认&lt;/h3&gt;
&lt;p&gt;哪些数据该删、何时删除，需要业务先作判断，回收站无法替代这一环节。我们通过看板展示数据集的冷热分布、访问衰减和容量增长趋势，供业务制定删除与扩容计划。确认后，业务可自助发起删除或恢复；删除的数据先进入回收站。&lt;/p&gt;
&lt;p&gt;看板依赖次日可查询（T+1）的元数据统计链路：我们先将 Meta 集群的文件信息全量导入湖仓建立基线，此后每天通过 changelog 增量同步。业务可直接查询容量、冷热分布和生命周期数据，无需存储团队代查。由于每周约有 200 PB 的新增和删除，这条链路一旦中断或发生数据漂移，就会影响容量及回收决策，因此必须保持稳定。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;精细化统计平台入湖管线&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;精细化统计平台入湖管线&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;误删恢复：7 天回收站&lt;/h3&gt;
&lt;p&gt;平台删除的数据保留在回收站 7 天。这个窗口既给误删留下恢复时间，也缓冲每日 PB 级的删除量，避免回收压力直接冲击元数据引擎。&lt;/p&gt;
&lt;p&gt;回收站针对不同路径采用以下机制：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;机制&lt;/th&gt;
&lt;th style="text-align: left;"&gt;设计&lt;/th&gt;
&lt;th style="text-align: left;"&gt;解决什么&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;免回收站路径（挂载参数）&lt;/td&gt;
&lt;td style="text-align: left;"&gt;训练临时目录等明确免恢复路径直接跳过回收站&lt;/td&gt;
&lt;td style="text-align: left;"&gt;防止回收站被海量删除垃圾撑爆&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;定时批量清理&lt;/td&gt;
&lt;td style="text-align: left;"&gt;按时间桶批量清理 7 天前数据，全程可观测，支持恢复&lt;/td&gt;
&lt;td style="text-align: left;"&gt;回收站容量治理与误删恢复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;流式删除定位&lt;/td&gt;
&lt;td style="text-align: left;"&gt;流式扫描定位已删除文件&lt;/td&gt;
&lt;td style="text-align: left;"&gt;误删秒级恢复与删除审计&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;保留期内，业务可通过恢复平台或命令行工具自助找回数据，无需运维工单。恢复通过元数据回滚和对象引用重建完成，无需复制数据本体；平台支持每日 PB 级恢复，单次 PB 级恢复可在小时级完成。删除与恢复均通过访问控制列表（ACL）校验权限，并记录审计日志。&lt;/p&gt;
&lt;h3&gt;空间回收：分布式垃圾回收（GC）&lt;/h3&gt;
&lt;p&gt;社区版 GC 依赖单机扫描，难以跟上每周约 200PB 的删除量。我们将待删除的文件和数据片段（slice）记录在 TiKV 中，再把回收任务拆成多个分片，交给多个工作节点（worker）并行处理。&lt;/p&gt;
&lt;p&gt;worker 通过租约领取任务，处理完成后删除对应的任务记录，再领取下一批。任务在完成前始终保存在 TiKV 中；即使某个 worker 故障，未完成的任务也能由其他 worker 接手。这样既分散了回收压力，也无需另建消息队列。&lt;/p&gt;
&lt;p&gt;租约决定由谁处理任务；对象能否删除，则由删除事务中的引用计数仲裁。只有引用计数达到可删除条件（当前实现为负值）时，才执行物理删除，避免并发处理或重试导致重复删除。对象存储侧通过批量删除、退避重试和审计记录完成回收。&lt;/p&gt;
&lt;p&gt;同样的分布式处理思路也用于大文件整理。长期追加的大文件可能累积多个 slice，增加读取开销。我们通过另一套 &lt;code&gt;gc_cluster&lt;/code&gt;，由 Manager 向多台 worker 分发 compaction 任务，定期在线合并这些 slice，以控制读放大。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;gc_slices 分片流水线&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;gc_slices 分片流水线&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;泄漏审计：leak-check&lt;/h3&gt;
&lt;p&gt;除按元数据记录执行物理回收外，我们还用 leak-check 审计遗漏对象。社区 fsck 检查“元数据有引用、对象却缺失”的情况；leak-check 则反向查找“对象存在、元数据已无引用”的情况。对象存储侧按 chunks/ 子前缀 32 路并发列举，缓解单连接扫描的瓶颈。我们分别取得对象清单和元数据引用清单，经两路 extsort 外部排序后流式归并，生成候选清单，无需在内存中构建千亿级全量 map。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;leak-check 双 extsort 归并&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;leak-check 双 extsort 归并&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;比对结果只是候选清单，不能直接删除：在途写入的对象可能暂时没有元数据引用。审计默认只生成清单，并过滤近期对象；确认后才执行删除。这样既能处理遗漏对象，也能降低误删在途写入的风险。&lt;/p&gt;
&lt;h2&gt;命题三：如何兼顾训练性能与 EB 级存储成本？&lt;/h2&gt;
&lt;p&gt;训练需要百 GB/s 量级的吞吐，还要处理数据集的顺序大 I/O 与 checkpoint 的随机小 I/O。在 EB 级容量下，数据不能全部放在热层；算力跨云部署也要求数据尽量就近访问。&lt;/p&gt;
&lt;h3&gt;三级缓存：从客户端到集中式集群&lt;/h3&gt;
&lt;p&gt;三级缓存按共享范围逐级扩大：一级缓存（L1）复用当前客户端的数据块，二级缓存（L2）通过客户端间的点对点（P2P）方式共享数据块，三级缓存（L3）集中承载训练热数据的读写。读取时依次查找 L1、L2、L3，三层都未命中才访问对象存储。&lt;/p&gt;
&lt;p&gt;多个训练任务读取同一数据集时，L1 缓存无法在客户端间共享，仍可能重复回源。L2 让客户端互为缓存节点：一个客户端已有的数据块，可供其他客户端通过按范围读取接口（getrange）复用。数十个任务并发读取相同数据时，回源量可接近一份，从而利用已有的本地盘和内存节省带宽。&lt;/p&gt;
&lt;p&gt;L1 和 L2 都未命中的请求进入 PB 级 L3 集中式缓存集群。L3 承接跨客户端共享的热工作集和主要读吞吐，并在同一集群中提供读缓存与写聚合，分别服务数据集的顺序大 I/O 和 checkpoint 的随机小 I/O；集群可通过水平扩展增加热数据容量。&lt;/p&gt;
&lt;p&gt;缓存故障可能使几十万客户端集中回源，放大对象存储压力。我们通过健康检查摘除异常节点，恢复后重新加入；若读缓存整体不可用，则降级为直读对象存储，以更高延迟换取访问连续性。同时记录各层命中量、字节数、延迟和慢请求，以识别缓存退化及回源变化。&lt;/p&gt;
&lt;h3&gt;元数据缓存：减少重复查询&lt;/h3&gt;
&lt;p&gt;训练任务读取文件时，不仅要获取数据块，还会反复查找文件、读取文件属性和打开文件（lookup、getattr、open）。这些操作都需要查询元数据。为减少对 TiKV 的重复访问，客户端用 open cache 缓存查询结果。&lt;/p&gt;
&lt;p&gt;难点是文件变化速度不同：数据集图片、打包文件通常长期不变，适合缓存得久一些；索引、日志等文件可能持续更新，需要尽快看到变化。如果所有文件使用相同的缓存过期时间（TTL），设得太长可能读到旧信息，设得太短又会让大量不变的文件反复回源。&lt;/p&gt;
&lt;p&gt;因此，我们按文件类型设置三档 TTL：静态文件使用较长的过期时间，动态文件使用秒级过期时间，其余文件使用默认值，配置支持 YAML 热更新。对静态文件，命中续租（expire_lease）会重置过期时间，让常用文件持续留在缓存中、冷文件自然过期；修改时间过滤（filter_mtime）则让最近修改、可能仍在写入的文件不进入缓存，避免缓存尚不稳定的元数据。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;按文件类型设置元数据缓存策略&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;按文件类型设置元数据缓存策略&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;控制读放大：预取与缓存协同&lt;/h3&gt;
&lt;p&gt;预取能加快顺序读取，但预取过多也会放大回源压力，因&lt;strong&gt;此我们在缓存层和内核层分别控制。缓存层根据对象 key 中的块序号提前加载后续数据块&lt;/strong&gt;，通过去重表和有界队列限制并发；队列满时丢弃预取，不阻塞正常读取。内核层的动态 readahead 根据顺序或随机访问调整窗口，受全局水位约束，配置可热更新。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;两级预取流水线&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;两级预取流水线&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;数据就近与冷热分层&lt;/h3&gt;
&lt;p&gt;前述改造提高了热数据的访问效率；要控制 EB 级容量成本，还需要决定数据放在哪里。当训练算力跨云部署时，我们让多个区域的桶共享同一文件系统命名空间，元数据记录对象的归属桶与存储层级。读请求据此路由到目标桶，因此数据迁移后业务访问路径无需改变。&lt;/p&gt;
&lt;p&gt;数据通过服务端桶间直连迁移，不占用客户端带宽；迁移期间，读路径由热层、冷层和全池遍历兜底。数据集或目录的归属桶及迁移策略由声明式配置管理。&lt;/p&gt;
&lt;p&gt;训练算力迁往其他云时，可提前将数据移至相应区域，降低跨云读取对专线带宽的依赖；低频数据则下沉到低成本桶或磁带库等冷数据池，并按需取回。这样既让热数据靠近计算，也控制 EB 级容量成本，容量扩展不再只依赖单一云厂商。此外，关键数据集还可保留跨云备份桶，作为云故障时的读取兜底。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;数据就近与冷热分层架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;数据就近与冷热分层架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;命题四：几十万客户端，如何接入和管理？&lt;/h2&gt;
&lt;p&gt;几十万客户端同时接入时，多租户权限、接入方式和客户端观测都需要重新设计。我们分别从权限控制、训练任务接入和客户端可观测性三个方面进行处理。&lt;/p&gt;
&lt;h3&gt;多租户：权限控制&lt;/h3&gt;
&lt;p&gt;由于生产环境的内核版本无法在 FUSE（用户态文件系统）挂载路径启用符合 POSIX（类 Unix 系统接口标准）的 ACL，我们将租户鉴权放到用户态的 ACL Manager。它以 Bucket 为授权单元，通过 READER、MAINTAINER 角色组管理权限，借助 guid 文件补充 LDAP（轻量级目录访问协议）组信息，并记录审计日志。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;ACL Manager&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;ACL Manager&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;这样，LDAP 侧只需维护组员关系；ACL 语义在用户态实现，绕开内核版本限制，共享文件系统中的越权访问在鉴权阶段被拒绝。代价是&lt;strong&gt;用户态鉴权增加开销&lt;/strong&gt;，我们用三层 inode 缓存缓解。&lt;/p&gt;
&lt;h3&gt;接入：S3 网关与免挂载 SDK&lt;/h3&gt;
&lt;p&gt;POSIX 挂载之外，我们提供基于 MinIO 插件层的 S3 Gateway 和进程内直读 SDK 两条接入路径。&lt;/p&gt;
&lt;p&gt;部分应用已使用 S3 协议，需要沿用现有接口访问同一份数据。S3 Gateway 将对象协议请求转换为文件系统操作，与 POSIX 挂载和 SDK 共享同一文件系统命名空间，并复用权限、缓存和底层数据。插件层实现目录级租户隔离和用户 token 校验，修复 ListObjectsV2 分页慢查询；我们还裁剪了非 S3 后端，形成轻量镜像。此外，针对跨云专线场景，我们提供 S3 协议的轻量读侧 SDK。&lt;/p&gt;
&lt;p&gt;另一条路径面向高并发训练。随着任务和 dataloader 进程增多，客户端同时活跃的数据连接达到百万级；FUSE 访问需经过内核态转发，短生命周期任务还要反复挂载和卸载，连接与挂载开销不断累积。为此，我们自研进程内直读的 Python SDK，让训练代码免挂载访问数据，连接随进程创建和退出。&lt;strong&gt;免挂载的代价是 SDK 需自行管理进程连接、fork 后的 session 和心跳。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SDK 随容器启动，通过 fsspec、ffrecord 接入现有数据加载流程。针对 dataloader 的 fork 场景，子进程重建 session 和心跳线程并独立上报；运行期间，SDK 定期上报指标，接收服务端下发的心跳间隔与限速策略。训练侧采用只读访问，多层拦截写操作；路径校验和跨桶隔离限制越权访问。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;Python SDK 免挂载直读架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;Python SDK 免挂载直读架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;指标观测与客户端升级&lt;/h3&gt;
&lt;p&gt;几十万客户端各自产生数百项指标。如果由 Prometheus 逐个抓取，时序规模会迅速增长。我们让客户端按分片向 MetricManager 上报指标，再由 Prometheus 抓取聚合结果。MetricManager 通过合并或舍弃部分细粒度标签控制时序规模，&lt;strong&gt;代价是部分观测维度无法继续保留&lt;/strong&gt;。聚合服务还需支持配置热更新、异常恢复、过载丢弃和过期清理，避免自身成为新的瓶颈。&lt;/p&gt;
&lt;p&gt;在几十万客户端规模下，发布升级也需要尽量降低对训练任务的影响。Gateway 和文件系统客户端（FS）镜像由同一构建链路产出；客户端无状态化配合 K8s 滚动重启，支持逐批升级：SDK 直读和 S3 网关路径对训练任务无感，POSIX 挂载路径的影响窗口可控。&lt;/p&gt;
&lt;p&gt;上述改造中，批量缓存接口、ReaddirFrom 流式扫描和动态 readahead 等通用能力，正在评估，整理后回馈社区；ACL、治理闭环等与业务耦合较深的能力，则继续在内部演进。&lt;/p&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;回顾上述实践，我们将应对 EB 级容量、千亿文件和几十万客户端规模的方法归纳为四点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;以分散化设计消除集中瓶颈&lt;/strong&gt;。我们让元数据读请求由 follower 分担，减少高频点查的 TSO 往返；优化热点 key，避免单节点被打爆；同时深入定制化元数据，提升整体能力。架构设计还需评估关键组件承担全局负载时的失效影响。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;以闭环机制治理数据生命周期&lt;/strong&gt;。看板支撑业务删除决策，7 天回收站与 PB 级恢复能力提供误删保护，GC 负责空间回收，leak-check 审计遗漏。恢复吞吐与删除规模相匹配，才能尽量把回收异常控制在空间收敛延迟范围内，降低误删损失与永久泄漏风险。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;以分层缓存和有界预取保障训练性能&lt;/strong&gt;。L1 客户端缓存、L2 客户端 P2P 缓存和 L3 集中式集群逐级吸收访问压力；预取则设置总量限制和水位约束，并在队列满时丢弃任务，避免额外读放大拖慢正常请求；同时通过数据就近和冷热分层降低跨云访问与 EB 级存储成本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;以权限和资源预算约束多租户影响。&lt;/strong&gt;ACL 明确访问范围，令牌桶限制各租户的元数据与数据面资源消耗；基于 RU（请求单元）的资源预算通过声明式配置管理，避免单个客户端影响共享集群。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;随着数据、算力和业务复杂度持续增长，我们将继续完善元数据引擎、数据面、性能工程、治理机制与可观测性，并在下一次数量级跃迁到来前完成必要的架构准备。持续识别并提前解决潜在瓶颈，是我们后续演进的重点。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Tue, 13 Oct 2026 06:55:37 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/user-stories/horizon-eb-scale-storage-practice</guid></item><item><title>JuiceFS 企业版 5.4：从千亿文件到百万客户端</title><link>https://www.juicefs.com/zh-cn/blog/release-notes/juicefs-enterprise-edition-v54</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;继 JuiceFS 企业版 v5.3 支撑千亿文件规模之后，v5.4 进一步在超大规模场景下提升多项能力。千亿文件规模下，单次操作的细小开销也会累积成显著的资源负担，元数据分区部署后需要协调不同节点，兼顾性能、数据一致性与稳定性。围绕这些问题，v5.4 版本包含下面多项新功能和改进：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;百万客户端同时挂载；&lt;/li&gt;
&lt;li&gt;海量文件规模的目录克隆；&lt;/li&gt;
&lt;li&gt;RDMA 带宽利用率超 90%；&lt;/li&gt;
&lt;li&gt;高并发随机读性能提升 100%；&lt;/li&gt;
&lt;li&gt;镜像文件系统支持数据按需同步；&lt;/li&gt;
&lt;li&gt;丰富缓存策略，适配更多业务需求；&lt;/li&gt;
&lt;li&gt;多项针对大规模数据的运维管理优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;01 支撑百万客户端接入&lt;/h2&gt;
&lt;p&gt;随着 Agent 走向规模化，JuiceFS 团队需要解决一个新的难题：&lt;strong&gt;在千亿文件的管理负载之上，承接百万客户端的连接与并发访问&lt;/strong&gt;。团队为此进行了持续探索与优化，并在企业版 v5.4 中发布这项能力。&lt;/p&gt;
&lt;p&gt;JuiceFS 采用元数据多分区分布式架构，将千亿规模文件的元数据分布到多个 Metadata 节点进行管理。为让这些节点同时承接大规模客户端连接，v5.4 将 Metadata 节点与网络代理（Proxy）合并部署，使客户端可以连接到任一 Metadata 节点。接入节点直接处理本分区请求，涉及其他分区的请求会内部转发至对应节点。多个客户端会话共用节点间的物理连接，减少连接数量及维护开销。&lt;/p&gt;
&lt;p&gt;除连接复用之外，大量客户端会话的查询、状态上报和过期清理也需要控制开销。v5.4 通过索引定位客户端会话，减少对无关会话的遍历；通过分页查询和采样上报控制状态管理开销；对过期会话分批清理，避免单轮清理工作过于集中。&lt;/p&gt;
&lt;p&gt;经过上述优化，JuiceFS v5.4 在压测中成功支撑了百万客户端同时接入。原先客户端分别连接多个 Metadata 节点，所需连接总数达数千万条。&lt;strong&gt;优化后，10 个内部 Proxy 节点承接百万条客户端连接，并通过连接复用，Proxy 到 Metadata Leader 仅需约 400 条后端物理连接，大幅降低了 Leader 的连接维护压力&lt;/strong&gt;。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;百万客户端测试&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;图片百万客户端测试架构图&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;02 快速克隆大型目录&lt;/h2&gt;
&lt;p&gt;用户在为训练数据创建分支或准备测试环境时，往往需要一份可独立修改的目录副本，当目录规模进一步扩大时，克隆本身也会变得更加复杂：&lt;strong&gt;对于采用多分区架构管理超大规模数据的用户，一棵目录树的元数据可能分布在多个 Metadata 分区上，克隆需要协调不同节点上的子树复制，保持完整的目录层级关系&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;v5.4 实现了跨分区目录克隆，通过递归处理各子树，保留原目录树的跨分区布局。克隆只要复制元数据，底层对象数据继续共享，无需复制；对副本的后续写入不会改变源文件的内容。&lt;/p&gt;
&lt;p&gt;在 30 个 Metadata 分区部署中，克隆 1 亿文件耗时 1 分 40 秒，&lt;strong&gt;平均速度为每秒 100 万个文件&lt;/strong&gt;。更多分区可以提供更高的并行处理能力，实际速度也受 Metadata 负载及目录树跨分区间分布情况影响。&lt;/p&gt;
&lt;p&gt;需要注意的是，跨分区目录克隆没有保证原子性。如果克隆期间源目录有持续的新数据写入，新克隆目录中的不同子树可能反映源目录在不同时间点的状态。对一致性有严格要求时，需要暂停后再执行克隆。&lt;/p&gt;
&lt;h2&gt;03 性能提升&lt;/h2&gt;
&lt;h3&gt;RDMA 高带宽传输利用率超过 90%&lt;/h3&gt;
&lt;p&gt;JuiceFS v5.3 开始支持在客户端与分布式缓存节点之间使用 RDMA，降低数据传输的 CPU 开销，提高传输效率。v5.4 继续提升带宽利用率、传输稳定性等多方面表现。&lt;/p&gt;
&lt;p&gt;在客户端配备 400 Gbps RDMA 网卡、分布式缓存节点配备 800 Gbps RDMA 网卡的测试环境中，单客户端读取带宽达到 45 GB/s，单个分布式缓存节点可提供 90 GB/s 的数据服务带宽，网卡利用率达到 90% 以上。&lt;/p&gt;
&lt;h3&gt;提升高并发随机读 IOPS&lt;/h3&gt;
&lt;p&gt;在多进程训练、HPC、渲染等场景中，高并发随机读不仅考验存储和网络能力，客户端的锁竞争与请求处理开销也可能限制 IOPS 的提升。&lt;/p&gt;
&lt;p&gt;过去半年，JuiceFS 团队通过简化高频处理逻辑、降低锁竞争，持续优化高并发随机读性能。&lt;strong&gt;优化后，随机读 IOPS 较此前整体提升一倍以上&lt;/strong&gt;：使用分布式缓存时，单客户端最高达到 18.9 万 IOPS；使用本地缓存时，单客户端最高达到 15.6 万 IOPS。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;随机读iops&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;随机读 IOPS 对比&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;测试环境：双路 AMD 服务器（128 核、256 线程），配备 1 TB DDR4 内存、100 Gbps 网卡及 3.84 TB NVMe SSD（缓存盘）；内核版本为 Linux 5.14。&lt;/p&gt;
&lt;h2&gt;04 多地域数据访问优化&lt;/h2&gt;
&lt;h3&gt;按需同步，减少数据复制&lt;/h3&gt;
&lt;p&gt;在多地域、多供应商、多集群的算力场景中，部署 JuiceFS 镜像文件系统后，镜像端能够自动同步源端的文件和目录。镜像端持续同步元数据，跟进目录结构和对象版本变化；此前，对象数据默认由后台全量同步。&lt;/p&gt;
&lt;p&gt;但镜像端的业务可能只需要源端的一部分模型、样本或历史目录。例如，源端有 10 PB 数据，镜像端只访问其中的 1%，全量同步就会传输并保存大量业务用不到的数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;v5.4 支持镜像文件系统按需同步对象数据。开启后，元数据仍持续同步，对象数据不再由后台自动同步&lt;/strong&gt;。客户端读取时，优先从镜像端对象存储获取所需数据；如果数据尚未同步，则从源端拉取，并按需保存在镜像端，从而减少不必要的数据传输与存储。&lt;/p&gt;
&lt;p&gt;如果业务要求首次访问就能获得本地读取性能，可以提前预热相关数据；需要保留完整数据副本的业务，则可继续使用后台同步模式。&lt;/p&gt;
&lt;h3&gt;通过只读节点加速元数据访问&lt;/h3&gt;
&lt;p&gt;对于只需加速元数据访问的业务，&lt;strong&gt;v5.4 提供了无需创建镜像文件系统的新模式&lt;/strong&gt;。用户可以在其他区域部署元数据服务节点（只读），为当地客户端加速元数据读取。客户端仍挂载源文件系统，加速节点支持动态扩缩容，便于根据访问需求调整部署规模。&lt;/p&gt;
&lt;h2&gt;05 更灵活的缓存管理&lt;/h2&gt;
&lt;h3&gt;提升热点缓存命中率&lt;/h3&gt;
&lt;p&gt;一次性扫描的数据通常很少再次使用，将这些数据写入缓存可能挤出反复访问的热点数据。默认情况下，请求未命中分布式缓存时，缓存节点会从对象存储拉取数据，并将其写入缓存。&lt;strong&gt;v5.4 新增 cache-aside（缓存旁路）模式&lt;/strong&gt;，开启后，未命中的请求改由业务节点直接从对象存储读取，不向缓存节点填充此次读取的数据。&lt;/p&gt;
&lt;h3&gt;批量增加缓存节点时保证命中率&lt;/h3&gt;
&lt;p&gt;同时新增多个缓存节点时，读取请求可能被分配到尚未缓存所需数据的新节点，触发对象存储回源。v5.4 新增 --commission 参数。新节点启用后，会保留扩容前的节点映射，在自身缓存未命中时向原节点获取数据，减少扩容期间的回源请求。&lt;/p&gt;
&lt;h3&gt;减少大容量缓存盘的文件数量&lt;/h3&gt;
&lt;p&gt;使用大容量缓存盘时，缓存块逐一保存为独立文件，会使缓存盘需要管理的文件数量持续增加。新增的 merge-cache 模式将多个缓存块合并存入大文件，并独立保存索引，从而大幅减少缓存盘上的文件数量。该功能目前处于 Beta 公测阶段。&lt;/p&gt;
&lt;h3&gt;按字节范围预热文件&lt;/h3&gt;
&lt;p&gt;如果任务只读取大文件中的一部分内容，预热整个文件会额外占用缓存空间并产生数据传输。v5.4 支持为预热文件指定一个或多个字节区间，只预热与这些区间相交的缓存块，使缓存准备范围与任务实际需要读取的内容相匹配。在此功能支持下，可以对 lance, parquet 等数据格式进行更精准的预热。&lt;/p&gt;
&lt;h2&gt;06 简化海量文件的运维管理&lt;/h2&gt;
&lt;h3&gt;回收站支持按删除时间恢复&lt;/h3&gt;
&lt;p&gt;从回收站恢复文件时，用户可能知道误删除发生在哪一段时间，却难以仅凭文件名或关键词准确筛选目标。restore 命令新增 start-time 和 end-time 参数，在原有关键词过滤之外，支持按文件删除时间限定恢复范围。&lt;/p&gt;
&lt;h3&gt;降低 GC / fsck 内存占用&lt;/h3&gt;
&lt;p&gt;检查大规模文件系统时，GC / fsck 需要处理大量记录，排序过程可能占用大量内存。v5.4 为这两个命令增加外排模式，利用磁盘参与排序，降低对内存的需求。GC 的外排模式仅允许在 dry-run 下执行，用于检查和评估，不实际删除数据。&lt;/p&gt;
&lt;h3&gt;通过 Token 白名单限制访问范围&lt;/h3&gt;
&lt;p&gt;v5.4 支持通过 Token 设置白名单，白名单可以指定为根目录下的若干一级子目录。使用该 Token 的客户端只能访问和修改这些子目录中的内容，从而限定不同团队或任务的数据访问范围。&lt;/p&gt;
&lt;h2&gt;07 小结：Scale changes everything&lt;/h2&gt;
&lt;p&gt;v5.3 将 JuiceFS 带入了一个新的规模阶段。与此同时，AI 的发展速度仍在不断刷新我们对数据规模和系统负载的认识。&lt;strong&gt;当文件数、客户端数和访问请求量同时进入更高量级后，系统所面临的性能、稳定性、一致性和运维复杂度问题不再彼此独立，而会相互影响，并随着规模扩大进一步放大&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;v5.4 进一步解决大规模场景下的系统级工程问题，覆盖客户端接入、跨分区目录、数据读取、镜像同步与缓存管理。云服务用户现已可以直接在线体验 JuiceFS 企业版 5.4，私有部署用户可通过官方渠道获得升级支持。&lt;/p&gt;
&lt;p&gt;JuiceFS 已广泛应用于 LLM 与多模态大模型、智驾、具身智能、量化金融等领域。我们会继续和这些前沿企业一起，持续完善 JuiceFS，让基础设施更好地支撑业务发展。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Mon, 28 Sep 2026 06:42:00 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/release-notes/juicefs-enterprise-edition-v54</guid></item><item><title>卓驭：基于 JuiceFS 百 PB 级智驾数据存储架构演进</title><link>https://www.juicefs.com/zh-cn/blog/user-stories/zhuoyu-pb-scale-autonomous-driving-storage-architecture-evolution</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;卓驭起步于 2016 年，致力于成为国际一流的移动物理 AI 公司。依托独有的原生多模态基础模型和生成式 AI 技术，打造可高效适配多场景的统一移动智能技术底座，不仅为车企提供开放、可定制、量产导向的智能辅助驾驶（涵盖 L2 及以上）解决方案，更致力于为不同场景下的自主移动机器人提供底层技术，实现从地面到近地空间的全域智能移动生态赋能。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;卓驭&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;随着研发规模扩大，训练和推理算力逐步走向多数据中心、混合云，数据规模和访问需求也持续增长。计算资源可以按需调度，但任务启动仍可能受挂载状态、数据回源和模型加载影响。&lt;strong&gt;如何让数据及时就绪、减少算力等待，成为存储架构演进的重要目标。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;卓驭基于 JuiceFS 构建统一数据底座，通过统一访问、多存储池和共享缓存，支撑数据接入、容量扩展与热点复用，再结合主动预热和读取优化，加快数据交付。目前，线上集群已承载百 PB 级数据，在线读取能力达到百 GB/s 量级。本文将围绕架构演进与生产实践展开。&lt;/p&gt;
&lt;h2&gt;01 智驾数据闭环：容量持续增长，数据反复消费&lt;/h2&gt;
&lt;p&gt;智能辅助驾驶的研发围绕数据闭环展开：路采车和量产车回传的数据经过筛选、标注，进入模型训练，再经过评测与发布；新问题和新数据又进入下一轮迭代。在这个过程中，数据在每个环节的就绪速度，直接影响算力是否需要等待，也影响研发效率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;从存储角度看，智驾研发是一条数据密集型链路&lt;/strong&gt;，多模态数据持续回流，并被不同研发任务反复消费。量产车和路采车回传的数据，加上仿真产出的数据，首先进入共享的多模态数据存储，再由筛选、标注、模型训练、仿真评测和个人研发环境等下游任务使用。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多模态数据流转与消费&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;智驾研发中的多模态数据流转与消费&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;随着数据规模和迭代频次增长，存储需要同时支撑持续扩容与高效读取，才能及时向下游任务交付数据。&lt;/p&gt;
&lt;h2&gt;02 原有架构中的访问、运维与扩展耦合&lt;/h2&gt;
&lt;p&gt;引入 JuiceFS 之前，我们同时使用 S3 接口和文件访问，多种入口并存，存储配置分散在业务侧，挂载客户端则由计算侧维护。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;存储入口与分散配置&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;演进前的多种存储入口与分散配置&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;由此我们归纳出三类核心痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;挂载与调度耦合&lt;/strong&gt;：任务启动依赖挂载客户端就绪，客户端维护延伸到计算侧，增加运维负担，并可能造成算力等待；  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储配置与业务耦合&lt;/strong&gt;：Bucket、Endpoint、凭证等存储细节进入业务代码，底层资源变化需要业务配合调整；  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容量与带宽扩展耦合&lt;/strong&gt;：容量持续增长，热点数据反复读取，本地缓存难以跨节点复用，容量与读取带宽难以按需独立扩展。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，新架构需要简化业务接入和挂载维护，并支持容量与读取带宽独立扩展。&lt;/p&gt;
&lt;h2&gt;03 架构升级：基于 JuiceFS 构建统一数据底座&lt;/h2&gt;
&lt;h3&gt;技术选型：以 S3 为核心，JuiceFS 为底座&lt;/h3&gt;
&lt;p&gt;为兼容存量业务并支撑后续扩展，我们选择 JuiceFS 作为存储底座，主要考虑以下四点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多协议访问&lt;/strong&gt;：支持 S3、POSIX 等接口，兼容不同业务的接入方式。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容量扩展&lt;/strong&gt;：底层依托对象存储，承载持续增长的数据。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存加速&lt;/strong&gt;：社区版已有本地缓存能力，可作为后续读取优化的基础。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开源生态&lt;/strong&gt;：社区活跃，便于结合生产需求持续改进。&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;1JuiceFS 架构图.drawio&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;JuiceFS 社区版架构图&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;统一访问层：应用与存储解耦&lt;/h3&gt;
&lt;p&gt;新架构中，业务通过内部数据平台的 SDK 访问存储。SDK 提供统一的逻辑命名空间，并处理底层存储位置与访问凭证的映射。通过 SDK 接入的业务以 S3 访问为主，减少计算侧的挂载维护；存量业务仍可使用 POSIX 接口。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;统一访问层与逻辑命名空间sdk&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;基于 SDK 的统一访问层与逻辑命名空间&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;以 S3 为核心的接入方式，也方便我们在入口进行流量隔离、监控和审计。&lt;/p&gt;
&lt;p&gt;统一访问层将应用与存储的变化分开，减少底层资源调整对业务接入的影响。在保持上层接口稳定的同时，我们可以继续扩展容量、增加缓存和接入外部数据。&lt;/p&gt;
&lt;h2&gt;04 核心能力构建&lt;/h2&gt;
&lt;p&gt;我们基于 JuiceFS 社区版扩展了多存储池、分布式缓存和 UFS，整体架构如下。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多存储池、分布式缓存与 UFS 的整合架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;多存储池、分布式缓存与 UFS 的整合架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;多存储池：容量扩展与故障隔离&lt;/h3&gt;
&lt;p&gt;我们在 JuiceFS 单卷的数据层增加路由，根据文件大小、权重等策略，将写入分配到不同存储池。后端可以是多个 S3 Bucket，也可以是文件存储。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;单卷多存储池的写入路由与容量扩展&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;单卷多存储池的写入路由与容量扩展&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;&lt;strong&gt;这样，JuiceFS 卷与 Bucket 形成一对多关系。当某个对象存储资源达到扩展边界时，可以增加存储池承接后续写入，已有数据不必整体搬迁。&lt;/strong&gt;多个独立存储池也有助于缩小单一资源域故障的影响范围。&lt;/p&gt;
&lt;h3&gt;分布式缓存：跨节点复用热点数据&lt;/h3&gt;
&lt;p&gt;初期我们使用本地缓存，节点之间无法复用缓存数据。任务调度到新节点后，如果本地没有所需数据，就需要再次回源，并在该节点保留一份副本。这既增加读取时延，也降低了缓存空间的利用率。&lt;/p&gt;
&lt;p&gt;为此，我们基于社区版开发了分布式缓存能力，多个客户端可以复用共享缓存中的热点数据，对象存储始终作为权威数据源，读取流程如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客户端通过一致性哈希选择目标缓存节点。  &lt;/li&gt;
&lt;li&gt;缓存节点本地盘命中时，直接返回数据。  &lt;/li&gt;
&lt;li&gt;未命中且允许回源时，由该节点从 S3 读取数据，返回后在缓存节点保留一份。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;客户端本地缓存仍然可用，与分布式缓存形成多级缓存架构。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;分布式缓存读取机制&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;分布式缓存读取机制&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;UFS（Under File System）：外部数据接入与缓存加速&lt;/h3&gt;
&lt;p&gt;我们还基于社区版开发了 UFS 能力，将外部数据源纳入 JuiceFS 的命名空间。UFS 负责外部数据的统一访问和缓存加速，权威副本继续保留在原系统中。&lt;/p&gt;
&lt;p&gt;例如，已有一批数据存放在 S3 中，希望使用 JuiceFS 的缓存加速，就可以将其映射到 JuiceFS 的某个路径下。原始数据无需迁移，业务通过映射后的路径访问即可。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;通过 UFS 将外部数据源映射到统一命名空间&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;通过 UFS 将外部数据源映射到统一命名空间&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;当前映射采用只读方式，支持的后端存储类型与社区版已有的后端支持保持一致。元数据可以按需加载，也可以提前预热；即使没有预先加载，访问目录时也能按需获取外部存储的目录和文件信息。&lt;/p&gt;
&lt;h2&gt;05 百 PB 级集群生产实践与优化&lt;/h2&gt;
&lt;p&gt;我们的线上集群已承载百 PB 级数据，带宽达到百 GB/s 量级，缓存容量达到 PB 级。围绕数据准备和任务读取，我们分别优化了缓存编排、模型配送、网络传输和数据格式。&lt;/p&gt;
&lt;h3&gt;缓存编排：随回传构建与主动预热&lt;/h3&gt;
&lt;p&gt;我们通过 SDK 统计发现，约八成数据会在回传后的八天内被使用。因此，我们在数据回传时异步构建分布式缓存，并根据访问规律规划缓存容量和 TTL，使业务读取尽量命中缓存。缓存到期或容量不足时，再进行回收。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;热点窗口与 TTL 驱动的缓存生命周期&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;热点窗口与 TTL 驱动的缓存生命周期&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;对于需要显式控制数据准备过程的任务，我们引入 Fluid 做主动编排。Dataset 描述数据集及其来源，应用可以通过对应的 Kubernetes PVC 访问数据。&lt;/p&gt;
&lt;p&gt;在任务流程中，DataLoad 配合 JuiceFS Runtime 完成预热，数据 Ready 后业务再开始消费。Fluid Controller 协调资源与状态，管理从缓存构建、业务消费到缓存回收的过程。对于未接入 Fluid 编排的业务，我们也提供 API，由业务自行控制缓存预热和淘汰。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;Fluid 缓存预热与生命周期编排&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;Fluid 缓存预热与生命周期编排&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;推理场景：大规模模型配送&lt;/h3&gt;
&lt;p&gt;在推理场景中，数据准备主要体现为模型配送：推理 Pod 需要完成模型加载后才能提供服务。之前，Pod 启动时直接从模型仓库加载模型，节点增加后，仓库需要承担大量重复读取。为此，我们将缓存放在模型仓库与推理节点之间。&lt;/p&gt;
&lt;p&gt;引入缓存后，模型先预热到分布式缓存，再通过缓存网络分发到各推理节点的本地缓存。同一数据块的请求通过一致性哈希落到同一缓存节点，可以在这里进行 I/O 聚合，减少重复回源。&lt;/p&gt;
&lt;p&gt;多个推理 Pod 加载同一模型时，可以通过共享缓存聚合读取请求，将模型仓库的回源数据量尽量控制在一份模型的数据量附近。&lt;strong&gt;在内部推理场景中，这套方案使部署速度较原有方式提升了十倍以上。&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;基于共享缓存的推理模型配送&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;基于共享缓存的推理模型配送&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;训练场景：TCP 零拷贝优化&lt;/h3&gt;
&lt;p&gt;数据进入缓存后，任务仍需要通过网络持续读取。为了减少这一过程中的开销，我们针对客户端与缓存节点之间的数据链路进行了优化，先从适用范围较广的 TCP 网络入手。&lt;/p&gt;
&lt;p&gt;普通读取路径需要通过 ReadAt 将数据从内核读到用户态缓冲区，再通过 conn.Write 写回内核发送。在服务端命中 cacheFile、且请求范围有效时，可以通过 io.CopyN → sendfile(2) 将文件数据直接送入 TCP Socket，省去这段用户态中转。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;缓存命中后的 TCP 零拷贝发送路径&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;缓存命中后的 TCP 零拷贝发送路径&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;为评估优化后缓存链路的整体性能，我们进行了 1M 顺序读压测。多服务端总带宽为 1200 Gbit/s，实测随着客户端负载增加，吞吐达到约 135–138 GiB/s。吞吐在约 135 GiB/s 时，1M 请求的 P99 时延约为 2 ms；继续提高到约 138 GiB/s 时，P99 时延升至约 20–30 ms。这说明接近带宽上限时，仍需在吞吐与尾延迟之间取舍。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;顺序读压测&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;顺序读压测&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;在 MLPerf Storage UNet3D 吞吐型负载中，我们首先采用单客户端高密度拓扑，将 8 个 accelerator rank 的并发读取压力集中到单节点、单条 200 Gbit/s 数据入口，验证单客户端的数据供给上限。测试覆盖 H20、H100 和 B200 三档负载；其中 H100 和 B200 已触及 200G 网络及客户端读取链路上限，瓶颈由后端存储转移至客户端数据入口。下一阶段将把 UNet3D 训练负载扩展至多客户端，并结合 RDMA，进一步验证大规模多机多卡训练下的持续供数能力与加速卡利用率。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;MLPerf UNet3D 理论带宽需求与实测结果&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;MLPerf UNet3D 理论带宽需求与实测结果&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;Lance 列存储：从数据格式层面减少 I/O&lt;/h3&gt;
&lt;p&gt;除了提高存储链路的吞吐，我们也通过调整数据格式，减少业务实际需要读取的数据量和 I/O 次数。&lt;/p&gt;
&lt;p&gt;以标注数据为例，同一个资产可能对应多个 JSON 文件，之前会将它们打成 tar 包存储。下游处理时仍需要逐帧解析 JSON，进行重复序列化和格式转换，产生大量小 I/O。&lt;/p&gt;
&lt;p&gt;我们将标注数据切换为 Lance，把下游常用的 JSON 字段展开为列。数据通过 Arrow Batch 在内存中传递，批量写入 Lance Table，下游按需选择列并批量读取，减少无关字段读取、逐帧 JSON 中转和重复序列化。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;逐帧 JSON 与 Lance 批量数据流对比&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;逐帧 JSON 与 Lance 批量数据流对比&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;不同流水线的收益有所差异：I/O 耗时下降约 20%–90%，IOPS 需求下降约 90%，带宽消耗下降 60% 以上，大幅降低了业务负载对存储资源的需求。&lt;/p&gt;
&lt;h3&gt;Lance 与 JuiceFS 结合预热&lt;/h3&gt;
&lt;p&gt;按列读取也为预热提供了更精确的范围。我们通过 Lance Manifest 等布局元信息定位所选列的物理字节区间，再映射到相交的 JuiceFS 数据块，只预热相关块，减少无关数据读取。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;Lance 列到 JuiceFS 预热数据块的映射&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;Lance 列到 JuiceFS 预热数据块的映射&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;相关能力已合并至 JuiceFS 社区版：按字节范围预热（&lt;a href="https://github.com/juicedata/juicefs/pull/7398"&gt;PR #7398&lt;/a&gt;），以及独立的 Lance 数据集解析工具（&lt;a href="https://github.com/juicedata/juicefs/pull/7399"&gt;PR #7399&lt;/a&gt;）。两者配合，可根据指定的数据集版本和列，生成文件及字节范围列表，用于按需预热。&lt;/p&gt;
&lt;h2&gt;06 总结&lt;/h2&gt;
&lt;p&gt;回顾这次实践，我们以 JuiceFS 为统一数据底座，沿三条路径逐步演进：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;稳定入口&lt;/strong&gt;：通过统一访问层屏蔽底层存储差异，使存储扩容和部署调整尽量不影响业务接入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;共享热点&lt;/strong&gt;：通过分布式缓存跨节点复用数据，减少任务迁移、扩容和多副本读取中的重复回源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主动就绪&lt;/strong&gt;：通过 Fluid 编排和 API 控制，让数据在业务消费前完成准备，以数据就绪状态衔接任务运行。&lt;/p&gt;
&lt;p&gt;这三条路径共同支撑了百 PB 级集群上的数据交付，百 GB 级以上的性能需求，也降低了存储运维与业务协同成本。目前，我们仍在持续优化缓存编排、网络传输和数据格式，让数据更快就绪、减少算力等待，为智驾研发提供更稳定的存储底座。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Wed, 16 Sep 2026 08:40:00 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/user-stories/zhuoyu-pb-scale-autonomous-driving-storage-architecture-evolution</guid></item><item><title>Agent Sandbox 规模化：JuiceFS 探索与实践</title><link>https://www.juicefs.com/zh-cn/blog/solutions/scaling-agent-sandboxes-juicefs-practice</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;过去一段时间，我们在和云厂商以及 Agent 团队推进 Sandbox 落地时，除了要解决数据如何进入 Sandbox，还需要考虑任务所需的数据和执行过程中产生的数据，如何跨任务、跨阶段持续使用。Sandbox 可以随任务快速创建和销毁，但数据往往需要继续保留、共享和流转。&lt;/p&gt;
&lt;p&gt;当 Sandbox 并发规模进一步扩大后，问题也不再只是数据持久化和共享访问，客户端规模、缓存复用、小文件以及故障隔离等运行问题开始变得更加突出。&lt;/p&gt;
&lt;p&gt;本文结合我们在 Agent RL（Agent Reinforcement Learning）场景中的实践，以及 JuiceFS 在云厂商 Sandbox 中的落地经验，介绍 Sandbox 数据的组织、跨云访问和挂载方式，并讨论大规模并发下遇到的问题和优化方向。&lt;/p&gt;
&lt;h2&gt;01 短生命周期 Sandbox，如何管理长生命周期数据？&lt;/h2&gt;
&lt;p&gt;以 Agent RL 为例，一次任务通常会经历任务分发、Sandbox 执行、轨迹采集、奖励评估和训练优化。任务执行产生的数据会继续被后续环节使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;首先，计算生命周期与数据生命周期开始分离&lt;/strong&gt;。Agent 任务启动时需要读取数据集、代码仓库、依赖环境、模型权重等内容；运行过程中又会持续产生执行轨迹、日志、中间文件和评测结果。Sandbox 可以在任务结束后销毁，但其中部分数据仍需保留，用于回放、分析、评估和后续训练。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;其次，数据共享与任务隔离需要同时存在&lt;/strong&gt;。每个 Sandbox 通常需要独立的工作目录和访问边界，但不同 Agent、Evaluator 或训练流程又可能访问同一份数据集、代码仓库，或复用前序任务产生的结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三，数据需要跨不同计算环境流转&lt;/strong&gt;。当任务分布在自建环境和不同云厂商的 Sandbox 中时，如果每个环境都维护独立的数据副本，不仅增加同步和管理成本，也会限制任务调度的灵活性。&lt;/p&gt;
&lt;h2&gt;02 JuiceFS 如何管理和接入 Sandbox 数据？&lt;/h2&gt;
&lt;p&gt;针对前面这些数据管理问题，下面介绍 JuiceFS 在实际 Sandbox 环境中的数据组织和接入方式。&lt;/p&gt;
&lt;h3&gt;用目录组织共享数据和任务数据&lt;/h3&gt;
&lt;p&gt;不同客户会根据自身业务特点采用不同的数据组织方式。结合现有客户的使用模式，一种比较典型的方式是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;datasets 目录&lt;/strong&gt;：用于存放评测数据集、训练数据集以及基准任务输入等核心数据；  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;repos 目录&lt;/strong&gt;：用于存放代码仓库、工具包以及相关依赖环境快照；  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务目录&lt;/strong&gt;：根据不同任务、Runtime ID 以及训练版本等信息，在文件系统中创建独立目录，用于保存每一次任务运行过程中产生的数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过这种方式，datasets、repos 等公共数据可以被多个任务共享访问，而每个任务又拥有独立的数据空间；同时，企业可以根据实际需求决定哪些数据需要长期保存，哪些数据可以在任务结束后进行清理，从而实现更加灵活的数据生命周期管理。&lt;/p&gt;
&lt;h3&gt;跨云 Sandbox 如何访问同一份数据&lt;/h3&gt;
&lt;p&gt;一些客户会基于安全、负载均衡、资源调度等因素，在多个云平台上同时使用不同厂商提供的 Sandbox 服务。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多云算力接入&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;多云算力接入&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;客户的核心数据（如训练集、推理数据等）通常存放在自有的数据平台或文件系统中，而不同云厂商的 Sandbox 环境则分别运行于各自的云基础设施之上。问题在于，不同云上的 Sandbox 如何访问同一份数据，而不需要分别维护数据副本。&lt;/p&gt;
&lt;p&gt;在这类场景中，可以利用 JuiceFS 企业版的跨云数据访问能力，将统一数据保留在自有平台，同时允许不同云环境中的 Sandbox 按需访问。借助细粒度权限控制，各 Sandbox 之间可实现数据隔离，而不同云上的数据集、代码仓库、评测数据等资源也可纳入统一命名空间进行管理。&lt;/p&gt;
&lt;p&gt;例如，当数据从 A 云环境写入后，B 云环境中的 Sandbox 可以快速读取并继续执行后续任务流程，从而提升任务调度的灵活性，也降低了多云环境下的数据管理复杂度。&lt;/p&gt;
&lt;h3&gt;面向强隔离 Sandbox 的 JuiceFS 数据挂载模式&lt;/h3&gt;
&lt;p&gt;要让 Sandbox 实际访问这些数据，还需要解决文件系统如何挂载到隔离环境中的问题。&lt;/p&gt;
&lt;p&gt;目前，云厂商提供的 Sandbox 方案与一些企业自研 Sandbox 存在一定区别。从现有云上 Sandbox 架构来看，多数方案基于 Firecracker 等轻量虚拟化技术实现，各云厂商会在此基础上进行不同程度的优化。&lt;/p&gt;
&lt;p&gt;这类 Sandbox 的核心特点是在虚拟机内部创建强隔离环境，使其与宿主机以及其他 Sandbox 实例之间保持隔离。因此，在这种强隔离架构下，数据访问不能简单依赖宿主机共享目录方式实现。&lt;/p&gt;
&lt;p&gt;针对这一特点，目前的接入方式是在每个 Sandbox 旁运行一个独立的 JuiceFS 客户端。客户端通过云厂商提供的 Sidecar-like 机制随 Sandbox 拉起，并向业务 Sandbox 暴露本地挂载路径。&lt;/p&gt;
&lt;p&gt;在该模式下，每个 Sandbox 均以独立身份对接后端 JuiceFS 服务，通过 VPC 网络访问部署在客户环境中的元数据服务，读取对应对象存储中的数据，并按任务需求挂载共享目录或独占目录。对于多任务、多组件间需要共享的数据，可通过共享目录实现统一访问，而各任务独有的数据则借助独立子目录进行隔离与保护，从而保障数据安全。&lt;/p&gt;
&lt;p&gt;在权限管理方面，JuiceFS 企业版提供的 Token 访问控制能力可进一步实现细粒度的权限管理，确保不同 Sandbox 之间的数据访问安全可控。&lt;/p&gt;
&lt;h2&gt;03 JuiceFS 在云厂商 Sandbox 中的实践&lt;/h2&gt;
&lt;p&gt;目前，我们已在腾讯云和阿里云的 Sandbox 环境中完成 JuiceFS 的落地实践。&lt;/p&gt;
&lt;h3&gt;腾讯云：Agent Runtime 中的数据挂载方式&lt;/h3&gt;
&lt;p&gt;在腾讯云场景中，其采用 Agent Runtime 机制，提供文件系统层、实例层和存储层三种不同层级的隔离模式：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一层是 Sandbox 内挂载块存储的方式&lt;/strong&gt;。该块存储完全由单个 Sandbox 独占使用，并且 Sandbox 会基于该块存储创建独立的文件系统。这种情况下，其他环境无法访问该文件系统，实现了较强的数据隔离。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二层是挂载腾讯云自身提供的存储服务&lt;/strong&gt;，包括 COS 对象存储以及 CFS 文件存储。通过 mount 的方式，可以将对应存储挂载到具体的 Sandbox 环境中，本质上也是在 Sandbox 内部完成挂载。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;腾讯云 Agent&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;腾讯云 Agent 沙箱文件隔离方案&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;&lt;strong&gt;第三种是 JuiceFS 的文件系统或子目录级隔离。&lt;/strong&gt; JuiceFS 可以将整个文件系统挂载到 Sandbox，也可以只挂载指定的子目录（subpath）。多个 Sandbox 因此可以共享同一个 JuiceFS 文件系统，同时通过不同子目录划分各自的数据访问范围，在共享数据与任务隔离之间取得平衡。&lt;/p&gt;
&lt;h3&gt;阿里云：FC 与 ACS 两种接入方式&lt;/h3&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;阿里云FC&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;阿里云FC Agent Sandbox&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;阿里云目前主要涉及两个产品线：其中一个是 FC（Function Compute，函数计算）Sandbox，其实现方式与腾讯云较为类似，同样采用 Sidecar-like 进程模式，在每个 Sandbox 中启动一个客户端，用于完成 JuiceFS 的挂载。&lt;/p&gt;
&lt;p&gt;另外一个产品线是 ACS（Container Compute Service，容器计算服务），其中也提供了 Sandbox 服务，但其实现方式有所区别。ACS 的方式是将支持 JuiceFS 挂载的能力集成到其驱动组件中。在实际使用过程中，由客户自行决定是否使用该能力以及具体的使用方式。后续相关代码也会计划放入其开源组件中。&lt;/p&gt;
&lt;p&gt;目前，无论是腾讯云还是阿里云，JuiceFS 的挂载能力都同时支持社区版和企业版。&lt;/p&gt;
&lt;h3&gt;Sandbox 生命周期管理与挂载控制&lt;/h3&gt;
&lt;p&gt;在目前的 Sidecar-like 客户端模式下，JuiceFS 的挂载会在 Sandbox 启动阶段完成，整个挂载生命周期由 Sandbox 平台统一托管。例如，在阿里云环境中由 FC 管控平台负责管理，在腾讯云中则由 Agent Runtime 平台承担这一职责。&lt;/p&gt;
&lt;p&gt;每个 Sandbox 对应一个独立的 JuiceFS 客户端，客户端随 Sandbox 创建并完成目录挂载。挂载过程中所需的 Token、挂载参数以及缓存目录等配置，均由 Runtime 平台统一注入和管理。&lt;/p&gt;
&lt;p&gt;在这一机制下，企业版与社区版存在一定差异。企业版通过控制台和 Token 等能力提供了更完善的管控方式，各云厂商的平台也基本将这些能力集成到了自身的挂载管理流程中。&lt;/p&gt;
&lt;p&gt;对业务侧而言，Sandbox 只需要看到挂载后的数据目录，而无需直接管理 JuiceFS 客户端。客户端的创建、配置、挂载和退出等过程均由 Sandbox 平台统一接管。&lt;/p&gt;
&lt;h2&gt;04 大规模 Agent Sandbox 场景下的挑战与优化方向&lt;/h2&gt;
&lt;h3&gt;客户端扩展瓶颈&lt;/h3&gt;
&lt;p&gt;当前一个 Sandbox 对应一个独立的 JuiceFS 客户端，每个客户端在企业版中均被视为独立客户端实例。随着 Sandbox 数量增长，客户端数量以及对应的进程、连接和元数据请求也会同步增加。&lt;/p&gt;
&lt;p&gt;然而，目前多数云厂商的 Sandbox 实现大多基于 Firecracker 虚拟化技术，而原生 Firecracker 并不支持类似 VirtIO-FS 的透传挂载方式。&lt;strong&gt;单个文件系统能够承载的客户端数量大约为 10 万级别&lt;/strong&gt;。目前，我们已收到部分客户关于百万级甚至更高规模 Sandbox 的需求，客户端架构的优化已成为后续演进的重要方向之一。&lt;/p&gt;
&lt;p&gt;一个正在探索的方向是引入类似 Mount Pod 的模式，在宿主机或指定节点上运行 JuiceFS 客户端，由多个 Sandbox 共享挂载能力。这样可以减少客户端实例数量和资源消耗，并进一步提升可支持的 Sandbox 规模。&lt;/p&gt;
&lt;h3&gt;缓存复用与数据加载效率&lt;/h3&gt;
&lt;p&gt;当前一个 Sandbox 对应一个独立的 JuiceFS 客户端，而 Sandbox 又可能分布在不同节点，因此客户端缓存很难在不同任务之间充分复用。在缺乏共享缓存层的情况下，大量重复的数据读取和加载压力会直接传导至对象存储；对于跨云 Sandbox 场景，还可能进一步增加跨云链路的带宽压力。&lt;/p&gt;
&lt;p&gt;针对这一问题，我们计划结合任务调度、宿主机磁盘资源以及 JuiceFS 的缓存能力，提高热点数据的缓存复用效率。例如，将相关任务尽可能调度至相近节点，并利用宿主机磁盘构建分布式缓存，让模型数据集等高频访问数据尽可能靠近计算，减少从对象存储重复加载。&lt;/p&gt;
&lt;p&gt;使用云厂商提供的 Sandbox，需要结合云厂商能够提供的缓存资源；自建 Sandbox，则可以根据客户自身基础设施进行规划，在性能、缓存容量和资源成本之间取得平衡。&lt;/p&gt;
&lt;h3&gt;小文件与元数据性能&lt;/h3&gt;
&lt;p&gt;在 Agent 执行过程中，日志、轨迹等数据往往表现为持续的小 I/O 写入、大量小文件生成以及频繁 flush，这对 JuiceFS 的元数据和写入性能提出了更高要求。&lt;/p&gt;
&lt;p&gt;针对此类场景，我们将与业务方共同探讨优化策略，例如评估是否可以将大量小 I/O 合并为较大粒度的写入请求，或通过批量写入方式减少频繁的小文件操作。从业务侧调整数据写入模式，也有助于降低底层元数据服务的压力。&lt;/p&gt;
&lt;h3&gt;故障隔离与平台化治理&lt;/h3&gt;
&lt;p&gt;在大规模并发场景下，需要避免单个异常任务影响共享缓存、元数据服务或对象存储。例如异常访问产生大量请求时，需要尽可能控制其影响范围，避免波及其他任务。&lt;/p&gt;
&lt;p&gt;如果后续进一步引入共享缓存、共享客户端或客户端池，原有的隔离边界也会发生变化。多个 Sandbox 共享底层资源后，需要重新考虑故障隔离、资源限制、访问权限以及共享客户端的生命周期管理。&lt;/p&gt;
&lt;p&gt;另一方面，随着越来越多的平台将 Sandbox 作为面向不同团队或客户的公共服务，数据管理也会进一步延伸到多租户场景。不同租户之间需要保持清晰的数据与权限边界，不同类型的数据也需要采用相应的生命周期策略。&lt;/p&gt;
&lt;p&gt;可观测性也需要随之完善，不仅要能够观察客户端和缓存状态，还需要了解整体存储使用情况、共享资源的运行状态以及异常影响范围，为后续的资源治理提供依据。&lt;/p&gt;
&lt;p&gt;从目前的实践来看，Sandbox 本身并不难做到快速创建和销毁，真正麻烦的是它背后的数据不能跟着一起消失。轨迹、日志、评测结果和训练样本还要继续被后续任务使用，这也让存储逐渐从“给 Sandbox 提供一个目录”，变成需要长期参与整个 Agent 数据链路。&lt;/p&gt;
&lt;p&gt;当规模从十万级继续向百万级发展时，Sandbox 客户端、独立缓存这些原本简单直接的设计也开始需要重新考虑。我们目前还在验证客户端复用、缓存和隔离等不同方案，也很希望和正在做 Agent Sandbox、Agent RL 或相关基础设施的团队交流各自遇到的问题。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Wed, 02 Sep 2026 09:02:01 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/solutions/scaling-agent-sandboxes-juicefs-practice</guid></item><item><title>如何通过 S3 和 WebDAV 协议访问 JuiceFS？</title><link>https://www.juicefs.com/zh-cn/blog/usage-tips/juicefs-s3-webdav-gateway-guide</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;不同应用访问数据的方式并不相同：有的依赖 POSIX 文件接口，有的基于 S3 API 构建，也有用户希望通过文件管理器浏览和编辑远程文件。JuiceFS 的多协议访问能力，正是为了让这些工具能够访问同一套文件系统。&lt;/p&gt;
&lt;p&gt;本文会先说明 S3 Gateway 与 WebDAV 共享的初始化链路，再说明二者的适用场景和常见映射关系，最后分别展开二者的实现原理与边界。&lt;/p&gt;
&lt;h2&gt;01 共用一条初始化链路&lt;/h2&gt;
&lt;p&gt;JuiceFS 底层由元数据引擎和对象存储组成：元数据引擎管理目录、属性、锁、会话等信息，对象存储保存实际文件数据块。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;1JuiceFS 架构图.drawio&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;JuiceFS 社区版架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;从实现上看，S3 Gateway 和 WebDAV 会先经过同一条服务初始化链路。服务进程根据元数据地址、监听地址，以及 subdir、read-only、cache、limits、access-log、metrics 等公共参数创建 JuiceFS 客户端，依次初始化元数据客户端、对象存储、数据块缓存和 VFS 配置，最终得到 fs.FileSystem。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;S3 Gateway 与 WebDAV 共用的服务初始化链路&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;S3 Gateway 与 WebDAV 共用的服务初始化链路&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;也就是说，二者的共同基础不是 FUSE 挂载点，而是一个带会话、缓存、指标和后台任务的原生 JuiceFS 客户端。S3 请求和 WebDAV 请求进入各自的协议适配层之前，已经共享同一个 JuiceFS FileSystem 和同一个文件系统命名空间。&lt;/p&gt;
&lt;h2&gt;02 适用场景与常见映射&lt;/h2&gt;
&lt;p&gt;共用初始化链路说明了二者如何接入同一个文件系统。实际使用时，更关键的是访问方式的选择：哪些场景适合 S3 Gateway，哪些场景适合 WebDAV，以及常见请求会如何映射到 JuiceFS 操作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux 应用、训练任务或需要完整文件系统语义的工作负载，优先使用 FUSE / CSI。  &lt;/li&gt;
&lt;li&gt;已基于 S3 SDK、AWS CLI、MinIO Client 或对象存储工具链构建的应用，优先使用 S3 Gateway。  &lt;/li&gt;
&lt;li&gt;桌面文件管理器访问、DAV/rclone 同步、Office/Apps 协作和简单共享，优先使用 WebDAV。&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;s3常见&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;S3 Gateway 常见接口与 JuiceFS 文件系统操作映射&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;WebDAV 与 JuiceFS 文件系统操作映射&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;WebDAV 常见方法与 JuiceFS 文件系统操作映射&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;03 S3 Gateway 原理：对象语义到文件系统操作&lt;/h2&gt;
&lt;p&gt;S3 Gateway 负责把 S3 协议请求转换为 JuiceFS 文件系统操作。下文将从请求链路、内部状态、关键语义转换和权限协同四个层面展开。&lt;/p&gt;
&lt;h3&gt;请求链路：MinIO S3 Server 到 jfsObjects&lt;/h3&gt;
&lt;p&gt;MinIO S3 Server 负责 S3 协议层处理，包括请求路由、签名校验、认证授权和 Multipart Upload 编排等能力。请求通过协议层处理后，会进入 JuiceFS 实现的 ObjectLayer，也就是 jfsObjects。jfsObjects 负责把对象层面的读写、列举、删除和属性访问，转换为 JuiceFS 文件系统调用。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;调用关系&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;MinIO S3 Server、jfsObjects 与 JuiceFS FileSystem 的调用关系&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h3&gt;状态保存：内部命名空间如何承载 Gateway 状态&lt;/h3&gt;
&lt;p&gt;在请求转换过程中，Gateway 不只访问用户可见的 object path，也会使用内部命名空间保存服务自身需要的状态。例如，.sys/tmp 用于临时写入，.sys/uploads 用于 Multipart Upload 状态，.minio.sys 用于保存 MinIO/IAM 相关元数据。&lt;/p&gt;
&lt;p&gt;这些内部路径不属于普通用户理解中的业务数据，但它们是 Gateway 维持上传过程、分片状态和权限配置所需的基础。&lt;/p&gt;
&lt;h3&gt;语义转换：PutObject、Multipart、Bucket 与 Metadata&lt;/h3&gt;
&lt;p&gt;S3 对象模型和文件系统模型并不完全一致，因此 Gateway 需要在几个关键位置做语义转换。&lt;/p&gt;
&lt;p&gt;PutObject 需要保证对象上传完成后才对外可见。为避免客户端读到未完成对象，JuiceFS Gateway 会先写入临时文件，并保存 metadata/tag 等对象属性，完成后再通过 Rename 提交到目标路径。借助文件系统 Rename 的原子性，Gateway 可以把对象写入从“未提交”切换到“已提交”。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;PutObject 写入流程&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;PutObject 写入流程：临时文件到 Rename 原子提交&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;Multipart Upload 需要把多个 part 合并为最终对象。JuiceFS Gateway 可以利用 CopyFileRange 等文件系统能力组合已写入的 part，并通过 Rename 提交最终文件。这样主要调整的是文件系统元数据关系，而不是重新搬运全部数据。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;Multipart Upload 分片合并&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;Multipart Upload 分片合并与最终提交流程&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;Bucket 映射用于处理对象命名空间和文件路径之间的关系。单桶模式下，文件系统名称对应 bucket；多桶模式下，顶层目录映射为 bucket。对象的 metadata、tag、ETag 等属性，则通过文件扩展属性 xattr 保存。&lt;/p&gt;
&lt;h3&gt;权限协同：IAM 与多实例&lt;/h3&gt;
&lt;p&gt;S3 Gateway 还需要处理身份和权限信息，包括 root 用户、普通用户、policy、service account 等。这些配置会通过内部路径保存在 JuiceFS 文件系统中，从而在多个 Gateway 实例之间复用。&lt;/p&gt;
&lt;p&gt;因此，多个 Gateway 实例可以共享同一套 IAM 配置；但每个实例会缓存 IAM 信息，权限变更并非瞬时同步，实际部署时需要考虑刷新周期。&lt;/p&gt;
&lt;h2&gt;04 WebDAV 原理：文件协议到 JuiceFS 操作&lt;/h2&gt;
&lt;p&gt;WebDAV 的起点与 S3 Gateway 不同。它面向文件访问场景，常见客户端包括 macOS Finder、DAV/rclone，以及部分 Office / Apps 客户端。由于 WebDAV 协议本身就包含文件、目录、属性等概念，它不需要像 S3 Gateway 那样先处理 bucket、object、key 与 path、file 之间的模型转换。&lt;/p&gt;
&lt;h3&gt;请求链路：从 HTTP 入口到 webdavFS&lt;/h3&gt;
&lt;p&gt;WebDAV 请求进入 JuiceFS 后，会先经过 net/http 与 indexHandler。这一层主要处理 HTTP 入口相关能力，例如认证、传输和目录访问请求的转换。&lt;/p&gt;
&lt;p&gt;之后，请求进入 x/net/webdav.Handler，由它完成 WebDAV 协议层处理，并把协议方法转换为 webdavFS 可以处理的文件访问请求。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;WebDAV 请求链路&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;WebDAV 请求链路：从 HTTP 入口到 JuiceFS 文件系统操作&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;进入 webdavFS 后，Handler 传入的文件访问请求会被转换为打开、创建、读写、重命名、删除和属性设置等 JuiceFS 文件系统调用。由于 WebDAV 协议本身接近文件系统语义，这里的重点不是重建对象模型，而是处理请求方法、文件打开标志和属性状态如何落到 JuiceFS 操作上。&lt;/p&gt;
&lt;h3&gt;状态边界：dead properties 与 LOCK&lt;/h3&gt;
&lt;p&gt;WebDAV 的适配不只涉及数据读写，还涉及协议自身的状态。dead properties 可以用 JSON 形式保存到 xattr，从而落到 JuiceFS 的文件属性体系中；但 LOCK 状态不同，它当前保存在服务进程内存中。单实例部署通常没有问题；多实例部署时，不同实例无法自动同步锁状态。&lt;/p&gt;
&lt;p&gt;这些限制通常不影响 Finder 浏览、简单共享或 DAV/rclone 同步；但如果业务强依赖 WebDAV 文件锁语义，需要在部署方式和客户端行为上单独验证。&lt;/p&gt;
&lt;p&gt;S3 Gateway 和 WebDAV 共享同一条 JuiceFS 客户端初始化链路，并访问同一个文件系统命名空间；差异在于，S3 Gateway 需要将对象语义转换为文件系统操作，而 WebDAV 更接近文件访问语义。因此，访问方式的选择最终应回到应用本身：它是按对象组织数据，还是按文件和目录操作数据。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Fri, 28 Aug 2026 03:06:54 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/usage-tips/juicefs-s3-webdav-gateway-guide</guid></item><item><title>Nydus + JuiceFS：AI 推理容器镜像加载从 116 秒降至 1.4 秒</title><link>https://www.juicefs.com/zh-cn/blog/solutions/nydus-juicefs-ai-inference-container-image-acceleration</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;在大规模 AI 推理服务中，当集群扩容时，新增推理实例从创建到承接请求，需要经历容器镜像准备、文件系统挂载、运行环境与推理框架初始化、模型权重加载等一系列冷启动过程。随着镜像体积和模型权重规模不断增长，数据准备的耗时日益突出；大规模扩容时，大量实例同时启动，还会进一步增加 Registry、网络和后端存储的并发压力。&lt;/p&gt;
&lt;p&gt;针对这些问题，行业已经形成了多种优化思路，包括缩小镜像体积、通过 P2P 分发缓解并发拉取压力，以及通过按需加载减少启动关键路径上的数据传输。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;本文进一步探索一种基于共享存储的方案：将 Nydus 的镜像按需加载能力与 JuiceFS 的分布式存储和缓存机制结合，通过共享存储和缓存复用减少大规模扩容中的重复数据读取，并缩短容器启动时间&lt;/strong&gt;。对于 AI 推理场景，容器镜像和模型权重还可以统一纳入同一套存储、缓存和预取体系，在加速两类数据加载的同时，减少分别建设和维护两套数据链路带来的复杂度。&lt;/p&gt;
&lt;h2&gt;01 OCI 镜像分发为什么慢？&lt;/h2&gt;
&lt;p&gt;传统 OCI 镜像通常以分层压缩包的形式组织数据。在镜像拉取模式下，节点需要先完整下载并解压所需 Layer，才能启动容器。&lt;strong&gt;然而实践表明，容器启动阶段实际只访问镜像中约 6%~20% 的文件，其余绝大部分传输与解压开销都属于“非关键路径”上的无效等待。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在大规模集群中，这种“先全量准备、再启动”的方式会带来三个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;冷启动时间长：GB 级镜像（如 AI 推理镜像），全量下载和解压会显著延长新实例的启动时间，耗时动辄数分钟。  &lt;/li&gt;
&lt;li&gt;Registry 瓶颈：当 HPA 爆发式扩容或大批量发布时，成百上千个节点同时向 Registry 请求 GB 级镜像 Layer，容易造成 Registry 性能瘫痪与网络拥堵。  &lt;/li&gt;
&lt;li&gt;网络与存储冗余膨胀：节点间缺乏高效的 P2P /分布式共享复用机制，相同 Layer 在集群网络中被反复重复传输，造成严重的带宽与存储空间浪费。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;02 Dragonfly + Nydus：组合解决了什么，遗留了什么&lt;/h2&gt;
&lt;p&gt;面对大规模集群并发拉取镜像带来的 Registry 带宽压力，一种常见方案是引入 Dragonfly 等 P2P 分发系统。其核心思路是通过调度器和节点侧代理构建 P2P 分发网络，让节点之间共享镜像数据，从而分担 Registry 的出口流量。&lt;/p&gt;
&lt;p&gt;但 P2P 分发主要优化的是数据传输路径，并没有改变传统 OCI 镜像需要先下载、解压所需 Layer 再启动容器的机制。Nydus 则从镜像加载方式入手，将“全量准备后启动”转变为“先获取轻量元数据，再按需读取实际数据”，减少启动关键路径上的数据传输。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两者结合后，Nydus 负责按需加载，Dragonfly 负责分发按需读取的数据，在降低镜像实际读取量的同时，也缓解大量节点并发访问 Registry 的压力。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不过，这套组合也会引入额外的基础设施和运维复杂度。除了 Nydus Snapshotter，还需要部署和维护 Dragonfly 的调度、分发及节点侧组件，并处理 P2P 网络连通、缓存空间管理、节点异常以及缓存回收等问题。在较大规模的 GPU 推理集群中，这意味着团队需要额外维护一套独立于存储系统之外的数据分发和缓存体系。&lt;/p&gt;
&lt;p&gt;这也带来一个新的思路：&lt;strong&gt;如果集群已经部署了成熟的分布式文件系统（如 JuiceFS），是否可以将镜像数据的分发和缓存交给共享存储体系，在复用已有存储基础设施的同时，减少额外 P2P 分发组件的建设和运维成本？&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;03 架构重塑：Nydus 数据块由 JuiceFS 承载&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Nydus + JuiceFS 方案的核心思路，将原本依赖 P2P 网络调度的镜像数据分发，转变为基于分布式文件系统的共享存储与缓存复用。&lt;/strong&gt;在该架构中，JuiceFS 作为底层统一数据引擎承载 Nydus 的镜像 Blob 数据块，Worker 节点则通过标准 POSIX 接口读取数据。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;数据流动&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;Nydus + JuiceFS 架构与数据流向&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;镜像通过 CI/CD 阶段构建并转换为 Nydus 格式后，会被拆分为两部分。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Bootstrap（元数据）：体积极小（通常仅数百 KB，如 ~450KB），存放在 Image Registry 中，负责快速索引。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Blobs（实际数据块）：涵盖所有实际文件内容（通常为数 GB，如 ~1GB），在转换完成后直接持久化到 JuiceFS 共享文件系统中（例如 /var/lib/nydus/blobs）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当 Worker Node 启动容器时，containerd 触发 nydus-snapshotter，由 nydusd 通过 FUSE 挂载容器只读层。随后，当应用发起实际读请求时，nydusd 根据 Bootstrap 中的索引，通过 JuiceFS 挂载点按需读取对应的 Blobs 数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这条路径的关键变化是：Registry 仅负责 Manifest 和极轻量的 Bootstrap 分发；海量 Blobs 数据的传输完全交由 JuiceFS 的分布式缓存体系承载，从根源上消除了高并发拉取对 Registry 的网络瓶颈。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对于 AI 推理场景，这套架构还可以进一步统一镜像与模型数据的存储路径。除 Nydus 镜像 Blob 外，&lt;strong&gt;模型权重也可以存储在 JuiceFS 中，&lt;/strong&gt;使两类数据复用同一套存储、缓存和预取机制，在加速数据加载的同时，减少分别建设和维护两套数据链路的复杂度。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;维度&lt;/th&gt;
&lt;th style="text-align: left;"&gt;OCI 全量镜像拉取方案&lt;/th&gt;
&lt;th style="text-align: left;"&gt;Dragonfly + Nydus&lt;/th&gt;
&lt;th style="text-align: left;"&gt;JuiceFS + Nydus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;底层核心思想&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;镜像 Layer 全量拉取 + 分层解压&lt;/td&gt;
&lt;td style="text-align: left;"&gt;网络层 P2P 分流 + 镜像按需加载&lt;/td&gt;
&lt;td style="text-align: left;"&gt;存储层解耦收敛 + 分布式缓存 + 按需加载&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;容器冷启动方式与耗时&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;需等待全量下载并解压 一般需要 30s ~ 10min+&lt;/td&gt;
&lt;td style="text-align: left;"&gt;仅下载数百 KB 的 Bootstrap 元数据和启动所需的 Blobs 耗时通常需秒级&lt;/td&gt;
&lt;td style="text-align: left;"&gt;仅下载数百 KB 的 Bootstrap 元数据和启动所需的 Blobs 耗时通常需秒级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;镜像数据传输路径&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Registry → Worker 本地完整下载与解压&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Registry / 数据源 → Dragonfly P2P 网络 → Worker 按需读取&lt;/td&gt;
&lt;td style="text-align: left;"&gt;JuiceFS → Worker 通过 POSIX 接口按需读取，由本地及分布式缓存加速&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;核心组件&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Registry + containerd&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Nydus, Manager, Scheduler, Seed Peer, dfdaemon 等组件&lt;/td&gt;
&lt;td style="text-align: left;"&gt;JuiceFS + nydus-snapshotter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;镜像与模型权重协同方式&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;镜像从 Registry 拉取至节点本地，模型权重通过 NAS、对象存储等独立链路加载，两类数据分别管理&lt;/td&gt;
&lt;td style="text-align: left;"&gt;镜像通过 Nydus 按需加载，并由 Dragonfly 优化分发；模型权重仍依赖独立的外部存储链路&lt;/td&gt;
&lt;td style="text-align: left;"&gt;镜像 Blobs 与模型权重统一存储于 JuiceFS，并复用同一套缓存和预热机制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;运维复杂度&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;大规模并发时，易压垮 Registry 带宽&lt;/td&gt;
&lt;td style="text-align: left;"&gt;需要额外维护 P2P 调度、节点网络及缓存体系&lt;/td&gt;
&lt;td style="text-align: left;"&gt;可复用已有 JuiceFS 存储与缓存基础设施，减少独立镜像分发组件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;节点本地磁盘压力&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;高  需要存储全量镜像及解压后的本地数据&lt;/td&gt;
&lt;td style="text-align: left;"&gt;高且不可控 需作为 P2P 节点开辟 Cache 空间，剧烈扩缩容易 Disk Full&lt;/td&gt;
&lt;td style="text-align: left;"&gt;可控 统一共享存储池，使用本地 NVMe 盘组成 P2P 分布式缓存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;网络环境要求&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;普通 TCP&lt;/td&gt;
&lt;td style="text-align: left;"&gt;要求节点间大范围开放 P2P 端口，跨 VPC/NAT 环境易失效&lt;/td&gt;
&lt;td style="text-align: left;"&gt;标准局域网，节点间拥有常规局域网高带宽即可，无侵入性网络要求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;&lt;strong&gt;适用场景&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;无冷启动要求、集群规模小（&amp;lt; 20 节点）的通用业务&lt;/td&gt;
&lt;td style="text-align: left;"&gt;跨数据中心/跨云/边缘计算等无统一高带宽存储的超大规模集群；团队有较强的运维管理能力&lt;/td&gt;
&lt;td style="text-align: left;"&gt;跨数据中心/跨云/边缘计算等无统一高带宽存储的超大规模集群已部署或适合部署 JuiceFS，希望实现“镜像+模型”统一管理并极简运维&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;04 运行机制：Nydus 与 JuiceFS 如何协同&lt;/h2&gt;
&lt;h3&gt;启动阶段：获取 Bootstrap（元数据）&lt;/h3&gt;
&lt;p&gt;应用启动时，&lt;code&gt;nydusd&lt;/code&gt; 只需要从 Registry 获取轻量的 Bootstrap 文件树。Bootstrap 描述镜像文件树和数据块索引，但不包含任何实际文件内容。&lt;/p&gt;
&lt;p&gt;容器根文件系统（Rootfs）挂载完成后，即可进入后续启动流程。在我们的测试场景中，容器可以在数百毫秒内完成拉起并通过健康检查，无需在启动前完成大体积镜像数据的全量传输。&lt;/p&gt;
&lt;h3&gt;读取阶段：按需访问 Blobs（数据块）&lt;/h3&gt;
&lt;p&gt;当应用进程真正发起读操作（如 &lt;code&gt;open()&lt;/code&gt; / &lt;code&gt;read()&lt;/code&gt;）时，nydusd 通过 Linux FUSE 机制捕获 I/O 请求，根据 Bootstrap 索引定位到对应的 Blobs 数据块与偏移量（Offset）。&lt;/p&gt;
&lt;p&gt;对上层应用来说，这个过程是完全透明的；对底层数据路径来说，读取粒度从完整镜像层变成了指定文件和偏移量（Offset）对应的数据块。&lt;code&gt;nydusd&lt;/code&gt; 直接访问 JuiceFS 挂载点（如 &lt;code&gt;/var/lib/nydus/blobs&lt;/code&gt;）读取目标块，彻底解决了运行时向 Registry 频繁 Fetch 的链路。&lt;/p&gt;
&lt;h3&gt;缓存阶段：通过 JuiceFS Cache Group 复用数据&lt;/h3&gt;
&lt;p&gt;JuiceFS 的分布式缓存组机制（Cache Group）可以在集群内复用已经缓存的 Blobs 数据。当缓存命中时，数据以近乎本地 NVMe 的速度回传给内核；当缓存未命中时，仅按需拉取指定偏移量的数据块并自动充填缓存。除了缓存未命中的自动填充机制，也可以采用提前预热 Blobs 数据到缓存的方式来保证按需读取 Blobs 数据块过程中能够全部命中缓存。&lt;/p&gt;
&lt;p&gt;对于 AI 推理场景，同一机制也可以用于模型权重文件的加载流程。&lt;/p&gt;
&lt;h2&gt;05 生产场景实测数据&lt;/h2&gt;
&lt;p&gt;生产环境中，我们针对大体积 AI 推理镜像，对上述方案进行了对比测试。&lt;/p&gt;
&lt;h3&gt;1. 容器启动耗时对比&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;OCI overlayfs&lt;/th&gt;
&lt;th&gt;Nydus + JuiceFS&lt;/th&gt;
&lt;th&gt;备注&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold Pull + Run&lt;/td&gt;
&lt;td&gt;116s&lt;/td&gt;
&lt;td&gt;16s&lt;/td&gt;
&lt;td&gt;OCI 使用公有云 Registry 服务； Nydus + JuiceFS 使用 JuiceFS + 公有云对象存储服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold Pull + Run （分布式缓存预热）&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;约 1.4s&lt;/td&gt;
&lt;td&gt;启动所需 Blobs 数据已预热至 JuiceFS 分布式缓存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warm Pull + Run&lt;/td&gt;
&lt;td&gt;约 0s&lt;/td&gt;
&lt;td&gt;0.22s&lt;/td&gt;
&lt;td&gt;OCI 为同 tag 已在节点； Nydus + JuiceFS 为 blobcache 本地缓存&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无缓存预热时，Cold Pull + Run 加速约 7 倍：&lt;/strong&gt; 在本地缓存和分布式缓存均未预热的情况下，Nydus + JuiceFS 将容器启动耗时从传统 OCI 方案的 116 s 缩短至 16 s。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;启动阶段数据读取量减少约 99.9%：&lt;/strong&gt; 在 Cold Pull + Run 场景下，启动阶段的数据读取量从 11 GB 降至 11 MB。传统 OCI 方案需要在启动前完成镜像 Layer 的下载和解压，而 Nydus + JuiceFS 只需获取 Bootstrap 元数据，并按需读取启动过程中实际访问的 Blobs 数据。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分布式缓存预热后达 1.4 秒极速启动&lt;/strong&gt;：当 Blobs 数据在 JuiceFS 分布式缓存命中时，容器启动耗时为 1.4 秒；而在节点本地存在 &lt;code&gt;blobcache&lt;/code&gt; 的热启动（Warm Pull + Run）场景下，启动仅需 0.22 秒。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 文件按需读取性能&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;传统 OCI（本地磁盘直读）&lt;/th&gt;
&lt;th&gt;Nydus + JuiceFS（命中分布式缓存 Cache Hit）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;文件首块数据读取延迟（TTFB）&lt;/td&gt;
&lt;td&gt;约 0.9 ms&lt;/td&gt;
&lt;td&gt;约 3.2 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;小文件 / 散碎配置加载（IOPS）&lt;/td&gt;
&lt;td&gt;基准&lt;/td&gt;
&lt;td&gt;贴近基准&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大文件顺序读取吞吐（Throughput）&lt;/td&gt;
&lt;td&gt;本地磁盘上限&lt;/td&gt;
&lt;td&gt;GB/s 级别（JuiceFS NVMe 分布式缓存）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;首字节响应延迟（TTFB）：分布式缓存命中时，首块数据读取延迟约为 3.2 ms，相比传统本地磁盘直读的 0.9 ms 增加约 2.3 ms。额外延迟主要来自 FUSE 数据路径和缓存访问，但整体仍保持在毫秒级，业务侧基本无感。  &lt;/li&gt;
&lt;li&gt;散碎小文件加载（IOPS）：性能表现高度贴近本地磁盘基准。在 Python 依赖库及配置文件密集读取的场景下，JuiceFS 缓存机制未带来任何明显的 IOPS 瓶颈。  &lt;/li&gt;
&lt;li&gt;大文件顺序读取（Throughput）：&lt;strong&gt;Nydus + JuiceFS 在缓存命中时可达到 GB/s 级顺序读取吞吐。&lt;/strong&gt;相比直接受单节点本地磁盘性能约束的传统 OCI 路径，JuiceFS 可以利用 NVMe 分布式缓存提供高带宽的数据访问能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;06 小结：按需加载与共享缓存的结合&lt;/h2&gt;
&lt;p&gt;Nydus 将镜像从全量拉取改为按需读取，JuiceFS 则将数据块放入共享存储和分布式缓存体系，两者结合后，容器冷启动耗时从 116s 降至 16s，分布式缓存预热后约为 1.4 s；启动阶段的数据读取量也从 11 GB 降至 11 MB。&lt;/p&gt;
&lt;p&gt;这一方案的价值不只在于加速镜像启动。对于 AI 推理场景，镜像 Blobs 与模型权重可以统一由 JuiceFS 承载，并复用同一套缓存和预取机制；在大规模扩容时，分布式缓存还可以减少多个实例对相同数据的重复读取，降低 Registry 和后端存储的并发压力。&lt;/p&gt;
&lt;p&gt;在缺少共享存储的环境中，Dragonfly + Nydus 仍是优秀的 P2P 替代方案；而在已有 JuiceFS 基础设施的集群中，Nydus + JuiceFS 能够以极低的运维成本，实现极致的镜像分发与模型加载加速。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Wed, 19 Aug 2026 08:24:00 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/solutions/nydus-juicefs-ai-inference-container-image-acceleration</guid></item><item><title>Agent 一切状态皆文件：哈啰 AI 基建中的 JuiceFS 存储实践</title><link>https://www.juicefs.com/zh-cn/blog/user-stories/agent-state-as-file-juicefs-storage-practice</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;随着 AI 业务发展，哈啰需要同时支撑模型训练、数据处理和 Agent 等多类负载。针对原有多套系统并存带来的数据分散、访问方式不统一和跨云流转复杂等问题，&lt;strong&gt;哈啰在生产环境中引入 JuiceFS，构建统一文件数据底座，连接 Notebook、训练、评测和 Agent 等环境。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其中，Agent 已成为重要的业务形态，并已覆盖多个实际业务场景；面向员工的个人 Agent“贾维斯·龙虾”也已在内部落地。与训练场景主要关注高性能 I/O 和海量文件访问不同，Agent 更关注任务过程中的数据流转、状态保存和结果沉淀。本文将重点介绍哈啰如何使用 JuiceFS 持久化用户人格、会话、记忆、Skill、执行轨迹和业务产物，并通过共享 Workspace 实现 Agent 状态与计算 Pod 解耦。&lt;/p&gt;
&lt;h2&gt;01 哈啰 AI 基建与存储现状&lt;/h2&gt;
&lt;p&gt;哈啰业务场景丰富，包含大量客服、审核、调度、分析和播报等工作。为支撑这些业务中的 AI 应用，哈啰逐步建设了覆盖模型开发、训练、推理和服务的 AI 平台与大模型平台。&lt;/p&gt;
&lt;p&gt;AI 平台覆盖 Notebook、训练、离线推理、在线推理和镜像构建等场景。Notebook 需要挂载用户目录和数据集，训练与推理任务需要读取输入数据、模型及中间结果，镜像构建过程也会产生可复用的缓存。&lt;/p&gt;
&lt;p&gt;大模型平台负责聚合不同模型服务，并提供模型调用和压测能力。模型压测过程中产生的数据和报告同样需要持久化保存。&lt;/p&gt;
&lt;p&gt;针对不同类型的数据和工作负载，哈啰主要采用三类存储：CPFS 提供高性能并行存储；&lt;strong&gt;JuiceFS 作为平台共享文件底座，主要承载 Notebook 环境、任务数据集、模型压测报告和镜像构建缓存等需要 POSIX 语义、多节点共享和统一命名空间的数据&lt;/strong&gt;；对象存储则主要承载多媒体附件等对象数据。目前，仍有少量开发机目录作为历史过渡方案，尚未迁移至统一存储体系。整体上，这套存储架构已经覆盖 AI 平台中的数据持久化与跨环境共享需求。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;哈啰 AI 基建中的存储架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;哈啰 AI 基建中的存储架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;h2&gt;02 Agent 业务带来的存储新需求&lt;/h2&gt;
&lt;p&gt;随着 Agent 逐步进入实际业务，哈啰的 AI 能力已从模型开发、训练和推理，延伸到能够调用工具并完成业务流程的 Agent。相关应用覆盖两轮车辆运维照片审核、顺风车行程录音审核等场景，在扩大审核覆盖范围、减少人工投入和降低服务成本等方面取得了实际效果。&lt;/p&gt;
&lt;p&gt;在垂直 Agent 落地的基础上，哈啰进一步推出面向员工的个人 Agent“贾维斯·龙虾”。员工可以通过钉钉或 Web 与龙虾交互，结合长期记忆、Skill、MCP 和 Cron 等能力，完成答疑、报表、审核和分析等日常工作。&lt;/p&gt;
&lt;p&gt;与面向单一业务流程的垂直 Agent 相比，个人 Agent 需要在多轮交互和连续任务之间持续保留状态。在 IT 支持场景中，龙虾需要保存用户问题、附件、会话、知识库检索结果和工具调用结果；在运营日报场景中，需要持续读取历史底表和脚本，并生成新的数据表、图表和报告；在运营归因场景中，还需要保留取数脚本、中间数据和最终分析结果，以便后续检查和重新执行。&lt;/p&gt;
&lt;p&gt;一个长期运行的 Agent 需要保存五类数据：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户人格和行为配置；  &lt;/li&gt;
&lt;li&gt;会话历史与运行状态；  &lt;/li&gt;
&lt;li&gt;可跨会话使用的长期记忆；  &lt;/li&gt;
&lt;li&gt;Skill、MCP、渠道和定时任务配置；  &lt;/li&gt;
&lt;li&gt;脚本、CSV、Excel、图表和报告等业务产物。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些状态具有明显的文件形态：人格以 Markdown 保存，会话历史使用 SQLite，Skill 由目录和脚本组成，任务配置采用 JSON，业务成果则以数据表、图表和代码等普通文件存在。Agent 的完整状态最终表现为一组持续产生、修改并相互关联的文件和目录。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这类长期运行的 Agent 需要一套统一的共享存储层，支持 POSIX 语义、SQLite WAL、原子重命名和大量小文件访问，并允许多个 Pod 共享同一份用户状态。即使计算 Pod 被销毁、重建或重新调度，用户的人格、会话、记忆和任务产物仍需持续保留。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;03 JuiceFS 如何承载 Agent 的完整状态&lt;/h2&gt;
&lt;p&gt;为满足上述需求，哈啰将现有的 JuiceFS 共享文件底座延伸至 Agent 场景，用于承载每位用户的完整 Workspace。平台按照员工账号为每位用户分配独立的龙虾目录，路径结构类似：&lt;/p&gt;
&lt;pre class="codehilite"&gt;&lt;code&gt;/userdata/aiplatform-jarvis-claw/user/&amp;lt;staff_id&amp;gt;/data/workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用户的人格、会话、记忆、Skill、任务配置和业务产物均保存在该目录中，龙虾 Pod 只负责计算。Pod 销毁、重建或重新调度后，只需重新挂载原有 Workspace，即可继续使用此前的状态，实现 Agent 有状态、计算 Pod 无状态。&lt;/p&gt;
&lt;h3&gt;Workspace 中保存哪些 Agent 状态？&lt;/h3&gt;
&lt;p&gt;Workspace 中包含 &lt;code&gt;AGENTS.md&lt;/code&gt;、&lt;code&gt;SOUL.md&lt;/code&gt;、&lt;code&gt;PROFILE.md&lt;/code&gt;、&lt;code&gt;HEARTBEAT.md&lt;/code&gt; 和 &lt;code&gt;MEMORY.md&lt;/code&gt; 等人格文件，用于描述 Agent 的身份、行为方式和用户偏好，并会被组合进 System Prompt。&lt;/p&gt;
&lt;p&gt;会话和运行状态主要保存在 &lt;code&gt;sessions/&lt;/code&gt;、&lt;code&gt;transcripts/&lt;/code&gt;、&lt;code&gt;dialog/&lt;/code&gt;、&lt;code&gt;tool_results/&lt;/code&gt;、&lt;code&gt;history.db&lt;/code&gt;、&lt;code&gt;chats.json&lt;/code&gt; 和 &lt;code&gt;inbox_events.json&lt;/code&gt; 等文件和目录中。其中，&lt;code&gt;history.db&lt;/code&gt; 使用 SQLite，并同时产生 WAL 和 SHM 文件。用户消息会在一轮任务开始时写入 &lt;code&gt;transcripts/&lt;/code&gt;，即使后续任务中断，已经产生的会话内容仍能保留。&lt;/p&gt;
&lt;p&gt;长期记忆保存在 &lt;code&gt;memory/&lt;/code&gt;、&lt;code&gt;digest/&lt;/code&gt;、&lt;code&gt;mem_agent/&lt;/code&gt;、&lt;code&gt;mem_session/&lt;/code&gt;、&lt;code&gt;mem_metadata/&lt;/code&gt; 和 &lt;code&gt;resource/&lt;/code&gt; 等目录中。龙虾基于 ReMe，以 Memory as File 的方式管理记忆：一部分由 Agent 在执行过程中主动写入，另一部分由系统定期回顾历史对话，提炼用户偏好和长期有效的信息，再写入 &lt;code&gt;digest/&lt;/code&gt; 等目录。文字稿将后一过程称为“Dream”。&lt;/p&gt;
&lt;p&gt;Workspace 中还保存 &lt;code&gt;cron_jobs.json&lt;/code&gt;、&lt;code&gt;cron_runs/&lt;/code&gt;、&lt;code&gt;mcp.json&lt;/code&gt;、&lt;code&gt;channels.json&lt;/code&gt;、&lt;code&gt;dingtalk_webhooks.json&lt;/code&gt;、&lt;code&gt;skills/&lt;/code&gt; 和 &lt;code&gt;skill.json&lt;/code&gt; 等任务与能力配置。Skill 包以 ZIP 形式保存在对象存储中，Pod 启动时再拉取并解压到用户 Workspace。&lt;/p&gt;
&lt;p&gt;Agent 生成的脚本、数据表、图表和报告，则保存在 &lt;code&gt;output/&lt;/code&gt;、&lt;code&gt;scripts/&lt;/code&gt; 以及相关的 Excel、CSV 和 SVG 文件中。实际 Workspace 中还包括浏览器数据、备份和迁移文件等辅助内容。这些文件和目录共同构成 Agent 的完整状态，并在任务执行过程中持续产生、修改和积累。&lt;/p&gt;
&lt;h3&gt;统一文件系统如何支持数据流转与运维？&lt;/h3&gt;
&lt;p&gt;将 Agent Workspace 统一保存在 JuiceFS 中，不仅实现了状态持久化和存算分离，也使其能够与 Notebook、训练和评测等环境共享同一套文件系统。&lt;/p&gt;
&lt;p&gt;在权限允许的情况下，Notebook 可以直接访问 Agent 的 Workspace。Notebook 中准备的数据可以直接交给 Agent，Agent 生成的数据表和脚本也可以继续用于分析或评测，无须在不同系统之间反复上传和复制。&lt;/p&gt;
&lt;p&gt;巡检、迁移、备份和按用户统计存储用量等工作，也可以通过 &lt;code&gt;ls&lt;/code&gt;、&lt;code&gt;du&lt;/code&gt;、&lt;code&gt;rsync&lt;/code&gt; 等常规工具完成。工程师能够直接查看会话、记忆、配置和任务产物，定位执行过程中的问题。&lt;/p&gt;
&lt;p&gt;不过，随着 Workspace 从普通数据目录演变为 Agent 可以主动读写并执行脚本的工作空间，平台除了保障文件持久化和共享，还需要建立相应的访问控制与执行边界。&lt;/p&gt;
&lt;h2&gt;04 共享 Workspace 的安全边界&lt;/h2&gt;
&lt;p&gt;Agent 不仅需要读取和修改 Workspace 中的文件，还可能通过 Shell、MCP 和 REPL 执行命令或代码。如果缺少有效约束，其访问范围可能超出用户 Workspace，进而触及其他用户的数据、服务源码及容器中的敏感文件。&lt;/p&gt;
&lt;p&gt;因此，哈啰从用户数据隔离、工具调用治理、文件访问控制和子进程管理四个层面建立安全边界。&lt;/p&gt;
&lt;h3&gt;如何隔离不同用户的数据？&lt;/h3&gt;
&lt;p&gt;哈啰通过 JuiceFS 与 OpenACL 管理文件系统权限，为每位用户划分独立的 Workspace，并控制不同用户可以挂载和访问的目录范围。通过这一机制，每个 Agent 只能访问获得授权的 Workspace，无法读取其他用户的数据。&lt;/p&gt;
&lt;p&gt;文件系统权限建立了用户之间的基础数据边界，但无法覆盖 Agent 在容器内部执行工具和代码时产生的全部访问行为。因此，在用户级权限之上，还需要进一步约束工具调用及其执行环境。&lt;/p&gt;
&lt;h3&gt;如何判断工具调用是否允许执行？&lt;/h3&gt;
&lt;p&gt;当模型决定调用工具时，系统不会立即执行，而是先由治理层通过 &lt;code&gt;assert_policy()&lt;/code&gt; 对本次操作作出判断。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;Shell 工具调用的治理与沙箱执行链路&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;Shell 工具调用的治理与沙箱执行链路&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;治理结果包括四种类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALLOW&lt;/code&gt;：允许执行；  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;DENY&lt;/code&gt;：拒绝未知工具、&lt;code&gt;sudo&lt;/code&gt; 等危险操作；  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;ASK&lt;/code&gt;：请求人工审批；  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;SANDBOX_FALLBACK&lt;/code&gt;：转入沙箱环境执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要人工审批时，系统通过侧信道将审批请求发送至前端。用户批准后，经过泛化处理的规则可以写入 &lt;code&gt;policy.yaml&lt;/code&gt;，供后续同类调用继续使用。&lt;/p&gt;
&lt;p&gt;对于 Shell 操作，&lt;code&gt;execute_shell_command()&lt;/code&gt; 会将当前工作目录固定在用户 Workspace，而不是服务源码目录，并根据治理结果选择相应的沙箱配置。配置优先采用治理层动态生成的细粒度规则，其次采用服务启动时注册的全局默认配置；裸进程模式仅允许在本地开发环境中使用。&lt;/p&gt;
&lt;p&gt;这一层决定工具调用是否可以执行。对于获得许可的操作，平台还需要根据不同的运行方式限制其实际可访问的文件范围。&lt;/p&gt;
&lt;h3&gt;如何限制主进程与子进程的文件访问？&lt;/h3&gt;
&lt;p&gt;Agent 访问文件主要存在两种形式，需要分别采取不同的控制机制。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;read_file&lt;/code&gt;、&lt;code&gt;write_file&lt;/code&gt;、&lt;code&gt;edit&lt;/code&gt;、&lt;code&gt;grep&lt;/code&gt;、&lt;code&gt;glob&lt;/code&gt; 和 &lt;code&gt;view_image&lt;/code&gt; 等文件工具直接运行在服务主进程中，不会创建子进程，因此无法通过 bubblewrap 拦截。系统通过 &lt;code&gt;is_protected_path()&lt;/code&gt; 等应用层路径检查，将相对路径限定在用户 Workspace，同时阻止访问 &lt;code&gt;/app&lt;/code&gt; 等服务源码和安装目录。目录列举过程中，受保护的条目也会被过滤。&lt;/p&gt;
&lt;p&gt;Shell、stdio MCP Server 和执行模型生成代码的 REPL 则会创建子进程。对于这类操作，仅依靠应用层路径检查无法形成完整边界，因此系统使用 bubblewrap，通过 Linux Mount Namespace 为子进程构造独立的文件系统视图。&lt;/p&gt;
&lt;p&gt;在这一受限视图中，用户 Workspace 以可读写方式挂载，经过批准的其他路径按规则开放；&lt;code&gt;/app&lt;/code&gt; 等服务源码目录被隐藏或设置为只读，&lt;code&gt;~/.ssh&lt;/code&gt;、&lt;code&gt;~/.aws&lt;/code&gt; 等敏感路径则被禁止访问。由此，Agent 可以正常操作用户 Workspace，但无法访问容器中的全部文件。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;应用层路径守卫与沙箱隔离&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;应用层路径守卫与沙箱隔离的访问效果&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;线上环境如果声明使用 bubblewrap，但实际无法创建沙箱，Shell 和 MCP 会采用 fail-closed 策略，直接拒绝执行，不会退化为缺少隔离的裸进程。REPL 在执行模型生成的 Python 代码时同样要求沙箱可用，否则拒绝执行。&lt;/p&gt;
&lt;h3&gt;如何管理子进程的生命周期与沙箱边界？&lt;/h3&gt;
&lt;p&gt;每次需要创建子进程的工具调用，都会使用独立的沙箱和进程组，并启用 &lt;code&gt;--die-with-parent&lt;/code&gt;。当用户执行 &lt;code&gt;/stop&lt;/code&gt;、任务超时，或者同一 Scope 中的新任务取代旧任务时，系统会逐层取消当前任务，并通过 &lt;code&gt;killpg&lt;/code&gt; 清理整个进程组，避免 Shell、MCP 或其子进程继续在后台运行。&lt;/p&gt;
&lt;p&gt;stdio MCP Server 虽然由 SDK 自行启动，但其执行命令同样会增加 bubblewrap 前缀，与 Shell 使用相同的文件系统视图。REPL 执行模型生成的 Python 代码时也必须进入沙箱，从而避免 Shell 之外的执行方式成为绕过安全边界的路径。&lt;/p&gt;
&lt;p&gt;在具体实现中，使用 Tmpfs 覆盖 &lt;code&gt;/app&lt;/code&gt; 后，还需要将其重新挂载为只读，避免向临时 &lt;code&gt;/app&lt;/code&gt; 写入文件时出现静默成功；进入沙箱前，也需要清理指向受保护路径的 &lt;code&gt;VIRTUAL_ENV&lt;/code&gt; 和 &lt;code&gt;PATH&lt;/code&gt;，以避免 Python 运行环境异常。服务启动时还会在沙箱中实际执行 &lt;code&gt;/bin/true&lt;/code&gt; 完成自检，而不只是判断 bubblewrap 二进制文件是否存在。&lt;/p&gt;
&lt;p&gt;当前方案主要解决文件系统和服务源码的隔离问题，尚未通过 &lt;code&gt;--unshare-net&lt;/code&gt; 隔离网络；除执行超时外，也暂未设置 CPU 和内存配额。&lt;/p&gt;
&lt;h2&gt;05 小结&lt;/h2&gt;
&lt;p&gt;哈啰的实践可以归纳为四点。第一，Agent 一切状态皆文件。 人格是 Markdown，历史是 SQLite，Skill 是目录，记忆和配置是文件，业务产物是 Excel、CSV、图表和脚本。&lt;/p&gt;
&lt;p&gt;第二，共享文件系统实现状态持久化和存算分离。 Agent Pod 只负责计算，状态和 Workspace 保存在 JuiceFS 中。Pod 可以随时重建，用户数据不会随计算实例消失。&lt;/p&gt;
&lt;p&gt;第三，同一文件系统连接多个 AI 环境。 Notebook、训练、评测和龙虾可以访问同一套文件，数据不需要在不同系统之间反复复制，运维也可以回归常规文件操作。&lt;/p&gt;
&lt;p&gt;第四，开放 Workspace 的同时建立分层安全边界。JuiceFS 与 OpenACL 管理用户级文件系统权限，应用层路径检查限制主进程文件工具的访问范围，bubblewrap 控制 Shell、MCP 和代码执行产生的子进程。&lt;/p&gt;
&lt;p&gt;下一步，我们希望推动 Agent 自进化。Agent 的埋点、记忆和评测数据持续沉淀在文件系统中。模型会继续增强，而这些沉淀下来的数据将成为 Agent 后续演进的基础。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Tue, 11 Aug 2026 07:34:17 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/user-stories/agent-state-as-file-juicefs-storage-practice</guid></item><item><title>腾讯云 x JuiceFS：基于 FoundationDB 的企业级统一存储实践</title><link>https://www.juicefs.com/zh-cn/blog/user-stories/tencent-cloud-juicefs-foundationdb-enterprise-unified-storage</link><description>&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;随着大数据和 AI 业务发展，越来越多企业开始从存算一体架构转向存算分离架构。但在实际落地中，企业往往同时使用 COS、OBS、S3、HDFS 等多类存储系统，不同后端在协议、鉴权、文件系统语义和元数据性能上存在差异，给统一接入、数据利旧和缓存加速带来了挑战。&lt;/p&gt;
&lt;p&gt;针对这些问题，腾讯云团队基于 JuiceFS + FoundationDB 构建了一套企业级统一存储方案，既能为新建文件系统提供完整的 POSIX 语义，也能将已有对象存储和 HDFS 数据纳入统一命名空间，实现存量数据的统一访问与缓存加速。&lt;/p&gt;
&lt;p&gt;其中，FoundationDB 作为元数据引擎，支撑百亿级元数据管理和强一致事务，这也是 JuiceFS 社区首次分享将 FoundationDB 用于元数据管理的实践；本地缓存与分布式缓存则用于降低远端访问延迟。后续，团队还将围绕湖仓一体、AI 训练加速、智能分层和跨集群联邦等方向持续演进。&lt;/p&gt;
&lt;h2&gt;01 背景与挑战：存算分离之后，存储接入变复杂了&lt;/h2&gt;
&lt;p&gt;在前期客户沟通和 POC 过程中，我们发现，企业从存算一体迁移到存算分离时，最关注的并不是架构形态本身，&lt;strong&gt;而是两个更实际的问题：现有数据和业务能否平滑接入，切换后性能是否还能接近原有存算一体架构。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;企业往往同时使用 COS、OBS、S3、HDFS 等多类存储系统。不同后端在访问协议、SDK、鉴权方式和文件系统语义上存在差异，如果 Spark、Hive、Flink 或 AI 训练框架需要分别适配，客户端开发和维护成本会迅速上升。&lt;/p&gt;
&lt;p&gt;性能也是存算分离落地中的关键挑战。数据访问路径变长后，远端存储和网络访问会引入额外开销；而多存储并存又使缓存能力难以统一复用。因此，统一存储方案不仅要解决多后端接入问题，还要提供可复用的缓存加速能力。&lt;/p&gt;
&lt;p&gt;此外，对象存储虽然适合海量数据存放，但在 &lt;code&gt;list&lt;/code&gt;、&lt;code&gt;stat&lt;/code&gt;、目录遍历和小文件访问等元数据操作上，通常不如传统文件系统高效；不同后端对目录、权限、配额和快照等语义的支持也并不一致，这些差异会进一步影响大数据和 AI 应用的统一访问体验。&lt;/p&gt;
&lt;p&gt;基于这些需求，我们最终选择以 JuiceFS 构建面向企业场景的统一存储方案。&lt;/p&gt;
&lt;h2&gt;02 整体架构：统一接入与双模式设计&lt;/h2&gt;
&lt;p&gt;JuiceFS 采用元数据与数据分离的架构。元数据引擎可以根据业务规模和一致性需求进行选择，例如 Redis、TiKV 等；我们选择 FoundationDB，主要利用其强事务、有序 Key-Value、自动数据分布和横向扩展能力，支撑百亿级元数据管理。&lt;/p&gt;
&lt;p&gt;基于 JuiceFS 的基础架构，我们构建了由统一客户端、服务化元数据层、多级缓存和多后端存储适配组成的统一存储方案。JuiceFS 提供文件系统语义、多协议访问、对象存储数据路径和本地缓存等基础能力；在此基础上，我们进一步扩展统一鉴权、多卷管理、统一命名空间、已有数据接入和分布式缓存等能力，以满足云服务场景下的多租户和多存储需求。&lt;/p&gt;
&lt;p&gt;在客户端侧，大数据和 AI 应用可以通过 HDFS 接口、POSIX 挂载、S3 接口和 Python SDK 等方式访问系统。客户端支持 Hadoop SDK、Python SDK、FUSE 挂载、本地缓存和服务发现，使 Spark、Hive、Flink、Presto 以及各类 AI 训练框架无需分别适配底层存储。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;JuiceFS + FoundationDB&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;基于 JuiceFS + FoundationDB 的统一存储方案整体架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;在元数据访问路径上，我们将 JuiceFS 的元数据访问链路服务化。客户端不直接连接 FoundationDB，而是通过 RPC 与无状态元数据服务交互。元数据服务统一承载元数据 API、认证鉴权、多卷管理、配额管理、目录保护、文件保护和任务管理等能力，并通过 ZooKeeper 完成服务注册、服务发现和跨节点状态同步。元数据服务可以按需水平扩展，FoundationDB 则作为其后端元数据引擎，负责元数据持久化和事务处理。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;tt&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;托管模式与外部模式的数据访问架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;为了同时覆盖新建文件系统和已有数据接入场景，底层存储层提供托管模式和外部模式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;托管模式主要面向新建文件系统。文件元数据通过元数据服务写入 FoundationDB，文件数据按照 JuiceFS 的 &lt;code&gt;chunk / slice / block&lt;/code&gt; 模型切分后写入对象存储，从而提供完整的 POSIX 文件系统语义。  &lt;/li&gt;
&lt;li&gt;外部模式主要面向已有数据利旧场景，其核心是 UFS（Unified File System）抽象层。UFS 将 COS、OBS、S3 和 HDFS 等后端抽象为统一的文件系统接口，但不迁移或接管后端已有的数据和元数据，也不使用托管模式的切片存储路径。元数据类请求由元数据服务通过 UFS 透传到底层存储，数据读写则通过相应后端的原生协议完成。&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;UFS 多存储适配架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;外部模式下的 UFS 多存储适配架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;统一命名空间将两种模式组织在同一套目录结构中。对上层应用来说，看到的是统一的路径和访问入口；对底层实现来说，不同目录既可以对应托管文件系统，也可以挂载已有的 HDFS 或对象存储数据。这样，新增数据与存量数据可以在同一套访问体系下共存，而无需先完成大规模数据迁移。&lt;/p&gt;
&lt;p&gt;在数据访问路径上，系统同时提供本地缓存和分布式缓存。读请求优先查询本地缓存，本地未命中后访问分布式缓存，最后再回源到底层存储，从而降低远端访问延迟和后端存储压力。&lt;/p&gt;
&lt;h2&gt;03 FoundationDB 实践：面向百亿级元数据的设计取舍&lt;/h2&gt;
&lt;p&gt;在统一存储接入层中，元数据引擎决定了系统的规模上限和一致性能力。当文件数量达到十亿甚至百亿级时，元数据系统本身就会成为整个架构的关键瓶颈。&lt;/p&gt;
&lt;h3&gt;为什么选择 FoundationDB&lt;/h3&gt;
&lt;p&gt;在元数据引擎选型阶段，我们重点关注几个问题：单集群是否能够支撑百亿级元数据规模，事务模型是否足够强，是否支持自动分片和横向扩展，以及运维复杂度是否可控。&lt;/p&gt;
&lt;p&gt;传统关系型数据库在单卷规模和横向扩展上容易遇到瓶颈，因此我们重点评估了分布式 KV 方案，其中也包括 TiKV 和 FoundationDB。TiKV 在行业内已有不少大规模元数据场景实践，也具备较强的横向扩展能力；FoundationDB 则在严格事务一致性、有序 Key-Value 模型、自动数据分布、多副本强一致以及运维复杂度方面更符合我们的需求。&lt;/p&gt;
&lt;p&gt;最终，我们选择 FoundationDB 作为元数据引擎，主要是因为它更适合承载文件系统元数据这类强一致、高并发、可范围扫描的访问模式。与此同时，这次实践也让团队积累了 FoundationDB 在大规模元数据场景下的建模、调优和运维经验，为后续 Hive 库表元数据等更多场景提供了参考。&lt;/p&gt;
&lt;h3&gt;Key 设计：多卷隔离与范围扫描&lt;/h3&gt;
&lt;p&gt;文件系统元数据天然具有两类访问特点：一是同一集群需要承载多个文件系统，要求不同卷之间有清晰边界；二是目录遍历、属性查询、chunk 索引查询等操作经常依赖前缀扫描。&lt;strong&gt;因此，Key 设计既要满足多卷隔离，也要尽量贴合文件系统的访问路径，避免在大规模场景下引入热点或大事务问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;基于 FoundationDB 的有序 KV 特性，我们延续了 JuiceFS 元数据设计思路：以 &lt;code&gt;fsname&lt;/code&gt; 作为 Key 前缀区分不同文件系统；将同一文件相关的属性、目录项、chunk 索引等元数据组织在相近 Key 空间内，提升访问局部性；数值字段采用大端序编码，使字典序与数值顺序保持一致，便于顺序扫描。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;Key 编码设计&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;FoundationDB 元数据 Key 编码设计&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;在此基础上，多个文件系统可以共用同一个 FoundationDB 集群，同时保持各自独立的 Key 空间。对于 &lt;code&gt;list&lt;/code&gt; 等可能扫描大量数据的操作，则通过分页处理控制单次事务规模；事务冲突场景下，结合乐观锁和自动重试减少锁等待。&lt;/p&gt;
&lt;h3&gt;如何规避 FDB 的硬限制&lt;/h3&gt;
&lt;p&gt;FoundationDB 对 key、value 和事务大小都有明确硬限制：单个 key 最大 10KB，单个 value 最大 100KB，单个事务总大小最大 10MB。这些限制不能通过参数调整，&lt;strong&gt;因此在将其作为文件系统元数据引擎时，需要特别关注几类容易放大元数据规模的操作&lt;/strong&gt;：例如频繁随机写、反复 truncate、fallocate punch hole、copyFileRange 等，可能导致单个 value 持续增长；大范围文件拷贝、大文件截断、批量元数据导入或删除整个文件系统，则可能触发事务过大的问题。&lt;/p&gt;
&lt;p&gt;其中，风险最高的是 chunk slices 累积。在 JuiceFS 的数据模型中，一个 chunk 对应的 slice 列表会存储在同一个 value 中，每个 slice 约占 24 字节。当同一个 chunk 下累积到约 4,266 个 slices 时，就可能触及 FoundationDB 的 100KB value 限制。频繁随机写同一个 chunk、反复 truncate、fallocate punch hole、copyFileRange 等操作，都会持续增加 slice 数量。如果缺少治理机制，单个 value 就可能不断膨胀，最终导致事务提交失败。&lt;/p&gt;
&lt;p&gt;这个问题在实现上还有一个隐蔽点：部分路径会使用 &lt;code&gt;AppendIfFits&lt;/code&gt; 这类原子追加操作。它属于盲写，事务内无法提前知道 append 之后的 value 总大小；如果 append 后超过 100KB，往往要到事务提交阶段才会失败。这意味着问题不会在写入前及时暴露，而是随着 slice 静默累积，在某次提交时突然触发。因此，仅依赖失败后的重试并不够，还需要在数据模型和写入路径上提前设置安全边界。&lt;/p&gt;
&lt;p&gt;为了解决 chunk slices 累积问题，我们补齐并强化了 compact 机制。&lt;strong&gt;核心思路是在读写路径中提前发现 slice 数量过多的 chunk，并将多个小 slice 合并为更少的大 slice，从而控制单个 value 的增长&lt;/strong&gt;。对于轻度碎片化的 chunk，可以异步触发 compact，避免阻塞正常写入；当 slice 数量达到安全阈值时，则同步触发 compact，防止继续增长到 100KB 限制。实践中，&lt;code&gt;maxSlices&lt;/code&gt; 设置为 2500，对应约 60KB，为 100KB 上限预留了约 40KB 的安全空间。&lt;/p&gt;
&lt;p&gt;在实现 compact 时，还需要处理并发和回收问题。合并过程通过 CAS 语义确认 chunk 没有被并发修改，避免数据丢失；如果 compact 失败，不影响正常写入，可以在后续触发时重试。合并后产生的旧 slice 则进入 GC 流程，通过引用计数判断是否仍被使用，并结合延迟删除机制支持误删恢复和后台回收。&lt;/p&gt;
&lt;p&gt;除了 chunk slices 累积，我们也排查了文件系统配置、Kerberos Token、POSIX 锁、Xattr 扩展属性以及 UFS 路径类 key 等其他风险项。整体来看，这些风险相对可控：大部分 value 规模较小，key 设计也受到文件名或路径长度约束。真正需要重点治理的，仍然是 chunk slices 累积导致的单 value 膨胀问题。&lt;/p&gt;
&lt;h2&gt;04 缓存加速体系：用两级缓存降低远端访问开销&lt;/h2&gt;
&lt;p&gt;存算分离之后，数据访问路径从本地磁盘变成了远端对象存储或 HDFS。对于大数据分析和 AI 训练这类读密集场景，如果每次读取都直接回源到底层存储，网络延迟和后端访问压力都会被放大。&lt;strong&gt;因此，统一存储层除了要解决“如何接入多种存储”，还需要解决“如何让远端数据读得更快”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们的思路是引入两级缓存：客户端本地缓存作为 L1，分布式缓存作为 L2。读请求会先查询本地缓存，命中后直接返回；本地未命中时，再访问分布式缓存；如果分布式缓存仍未命中，才回源到底层存储，并将数据回填到缓存体系中。这样既能利用客户端本地 SSD 或内存提供最低延迟，也能通过独立的 SSD 缓存集群，在多个客户端之间复用热点数据。&lt;/p&gt;
&lt;p&gt;本地缓存主要沿用 JuiceFS 社区已有能力，并扩展支持外部模式。它运行在客户端进程内，默认以 block 为粒度缓存数据，支持 LRU 淘汰、顺序读预取和 OS Cache 控制。分布式缓存则是我们自研的独立缓存集群，客户端通过一致性哈希将相同数据块路由到固定缓存节点，更适合多节点共享读、客户端本地空间有限，或者需要跨任务复用热点数据的场景。&lt;/p&gt;
&lt;p&gt;缓存体系真正需要重点处理的是一致性。托管模式下，每次写入都会生成新的 Slice ID，缓存 key 随之变化，旧缓存自然不会被再次命中；同时对象存储中的底层对象写入后不会被原地修改，因此不需要额外的主动失效机制。&lt;/p&gt;
&lt;p&gt;外部模式则更复杂，因为底层 HDFS 或对象存储中的文件可能被外部系统直接覆盖。如果缓存 key 只包含路径，就可能读到旧数据。&lt;strong&gt;为此，我们在外部模式中引入 fingerprint 机制，将文件路径、block 偏移以及文件指纹一起纳入缓存 key&lt;/strong&gt;。fingerprint 由文件修改时间和长度组成，并在 open 时冻结；当文件被外部修改后，fingerprint 发生变化，旧缓存自然不命中，从而满足 close-to-open 一致性。&lt;/p&gt;
&lt;p&gt;这也带来一个取舍：托管模式基于 Slice ID 实现更细粒度的缓存隔离，而外部模式依赖文件 fingerprint，粒度更偏文件级，缓存失效范围可能更大。但对于已有数据可能被外部系统修改的场景，这是保证一致性所必须接受的代价，也是后续可以继续优化的方向。&lt;/p&gt;
&lt;p&gt;除了基础读缓存，系统还支持缓存预热、防击穿、TTL 驱逐和透明降级。例如，可以通过 warmup 命令提前预热热点数据；分布式缓存可以按目录前缀批量预热；当缓存层不可用时，系统也可以自动降级为直接读取底层存储，避免缓存故障影响业务读取。&lt;/p&gt;
&lt;h2&gt;05 多租户与弹性扩展&lt;/h2&gt;
&lt;p&gt;在云厂商场景下，统一存储层需要同时服务多个业务、多个租户和多个文件系统。因此，我们将元数据服务设计为无状态架构，客户端通过 ZooKeeper 完成服务发现和负载均衡，元数据服务节点可以按需水平扩展，从而提升整体访问能力和可用性。&lt;/p&gt;
&lt;p&gt;多卷隔离则依赖 FoundationDB 的 Key 前缀设计。每个文件系统使用独立的 &lt;code&gt;fsname&lt;/code&gt; 前缀，不同卷之间拥有独立的 Key 空间，使一个 FoundationDB 集群可以同时承载多个文件系统。新增文件系统时，也不需要重启元数据服务，可以在线完成扩展。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;多卷隔离架构&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;无状态元数据服务与 FoundationDB 多卷隔离架构&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;在多节点部署下，还需要同步配额、权限、目录保护、任务状态等运行时信息。我们通过 ZooKeeper 实现跨节点变更通知，并结合防抖合并、按 Key 精准刷新和定时轮询兜底机制，保证配置变更能够及时同步到各个元数据服务节点。&lt;/p&gt;
&lt;p&gt;通过无状态元数据服务、&lt;code&gt;fsname&lt;/code&gt; 前缀隔离和 ZooKeeper 状态同步，系统可以在保持多租户隔离的同时，实现服务层和元数据层的横向扩展。&lt;/p&gt;
&lt;h2&gt;06 百亿级元数据验证&lt;/h2&gt;
&lt;p&gt;我们基于 JuiceFS 1.3.0 和 FoundationDB 7.3 进行了百亿级元数据测试。测试环境采用 6 台 x86 服务器，操作系统为 Kylin V10 SP3，FoundationDB 采用 triple 三副本和 SSD 存储引擎部署。&lt;/p&gt;
&lt;p&gt;这组测试在已经写入 108 亿条元数据的基础上进行压测。结果显示，在百亿级数据规模下，FoundationDB 的单操作延迟与基准数据基本持平，部分操作如 &lt;code&gt;readdir_1k&lt;/code&gt;、&lt;code&gt;lookup&lt;/code&gt; 甚至表现更好。整体来看，单卷元数据规模达到 108 亿后，元数据操作延迟仍可以保持在亚毫秒到 2ms 级别，说明大规模数据量下没有出现明显性能衰减。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;benchmark 单操作&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;FoundationDB 的单操作延迟与基准数据&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;在极限写入测试中，我们通过 15 个并发进程执行 &lt;code&gt;juicefs clone&lt;/code&gt; 克隆元数据，验证 FoundationDB 的写入上限。测试结果显示，在 6 台机器 + SSD 配置下，稳定阶段写入吞吐约为 &lt;strong&gt;11,000～15,000 inode/s&lt;/strong&gt;。从瓶颈分析来看，主要压力集中在 Storage 写入队列和磁盘 I/O，而不是 CPU 或网络。后续可以通过增加 Storage 进程、提升磁盘性能，或扩展 Commit Proxy、TLog 等组件进一步提升读写能力。&lt;/p&gt;&lt;/div&gt;
&lt;div class="w-block-ImageWithCaption block-ImageWithCaption"&gt;&lt;dl&gt;
    &lt;dt&gt;image&lt;/dt&gt;
    &lt;dd&gt;创建速率&lt;/dd&gt;
    &lt;dt&gt;caption&lt;/dt&gt;
    &lt;dd&gt;inode 创建情况&lt;/dd&gt;
&lt;/dl&gt;&lt;/div&gt;
&lt;div class="w-block-markdown block-markdown"&gt;&lt;p&gt;高可用方面，我们分别验证了 1 台和 2 台机器故障场景。在 1 台机器故障时，服务保持可用，FoundationDB 会自动触发副本修复和数据迁移；在 2 台机器故障时，服务仍然可用，但集群容错能力会降为 0，不能再容忍更多故障。故障恢复后，系统会触发数据重新均衡，测试中完整恢复时间约为 4 小时。这个过程会带来额外 I/O 压力，因此生产环境需要重点关注磁盘水位、单盘故障和恢复期资源消耗。&lt;/p&gt;
&lt;p&gt;容量规划方面，测试数据显示，50 亿 inode 时 FoundationDB 磁盘占用约 6.1TB，108 亿 inode 时约 10.1TB。综合三副本、压缩效果和预留空间后，可以按 &lt;strong&gt;约 120GB / 亿 inode&lt;/strong&gt; 作为基础容量估算。&lt;/p&gt;
&lt;p&gt;通过这组验证，我们基本确认：FoundationDB 可以支撑单卷百亿级元数据规模，并在已有百亿级数据的情况下保持稳定的元数据访问性能。同时，三副本架构具备较好的故障恢复能力，但生产环境仍需要做好容量规划、磁盘监控和恢复期 I/O 管理。&lt;/p&gt;
&lt;h2&gt;07 小结与未来规划&lt;/h2&gt;
&lt;p&gt;目前，这套基于 JuiceFS + FoundationDB 的统一存储接入层，已经在大数据存算分离场景中完成了核心能力建设，包括统一客户端接入、外部数据利旧、多级缓存加速、百亿级元数据管理以及多租户扩展能力。后续，我们会继续围绕湖仓一体、AI 训练和存储治理等方向演进。&lt;/p&gt;
&lt;p&gt;在湖仓一体方向上，我们计划基于统一存储层加强与 Iceberg、Hudi 等表格式的集成，让上层数据湖和数据仓库场景能够复用统一的存储接入、权限管理和缓存加速能力。&lt;/p&gt;
&lt;p&gt;AI 训练方面，后续会进一步优化缓存预热和就近缓存能力，降低训练任务中的远端读放大和 GPU 等待时间。同时，也会结合快照能力探索模型 checkpoint 管理，让训练过程中的中间状态保存、恢复和复用更加高效。&lt;/p&gt;
&lt;p&gt;在存储治理方向上，我们还会探索智能分层能力，根据数据访问频率在 SSD、HDD 和对象存储之间自动迁移，从而在性能和成本之间取得更好的平衡。对于更大规模的云上部署场景，也会继续推进跨集群联邦能力，实现多集群之间的元数据同步、路由和统一访问。&lt;/p&gt;
&lt;p&gt;未来，我们会继续围绕性能、成本、弹性和数据治理能力迭代，让更多业务能够以统一方式访问和管理底层数据。&lt;/p&gt;&lt;/div&gt;</description><pubDate>Fri, 31 Jul 2026 06:04:00 +0000</pubDate><guid>https://www.juicefs.com/zh-cn/blog/user-stories/tencent-cloud-juicefs-foundationdb-enterprise-unified-storage</guid></item></channel></rss>