← 文章 / 云原生与基础设施
Meta 工程博客 3小时前 · 2026-08-31 13:09:23 · 1 阅读

Meta 大规模 AI 存储架构蓝图

过去几年,模型能力与训练数据集规模呈指数级增长。过去一年左右,发布新一代前沿模型的间隔已从几个月缩短到几周。可靠且高速的存储访问对 AI 创新的速度和计算成本都至关重要。如果说 AI 是大脑,存储就是记忆:能力与速度在很大程度上取决于记忆容量和检索速度。

然而,尽管 AI 计算性能大约每两年翻三倍,存储和互连的性能增长却相对缓慢。因此,存储瓶颈依然是 AI 工作负载中 GPU 停滞的主要原因之一,直接影响开支和上市时间。除了 GPU 利用率,存储架构还直接影响 AI 研究的迭代速度:随着 GPU 越来越跨地域分布、数据集规模越来越大,研究人员需要花费大量时间在区域间摄取和搬运数据,从而拖慢了研究节奏。在本文中,我们将讨论 Meta 的 BLOB 存储架构是如何演进,以应对两大核心挑战:最大化 GPU 利用率,以及最大化研究速度。

存储架构概览

Meta 运营着数百个 EB 级存储集群,为 Facebook、Instagram、Reality Labs、Meta AI、广告、数据仓库以及内部数据库等所有外部和内部产品提供服务。我们的存储服务对外暴露对象存储、文件系统和块设备 API,这些 API 抽象构建在一个可横向扩展的底层块存储层 Tectonic 之上。Tectonic 层是区域级、多租户的存储底座,通过纠删码技术提供高持久性和高可用性,支持跨介质类型(如 HDD 和闪存)的分层,并在租户间对热、温、冷数据进行智能放置,以高效利用 I/O。运行在 Tectonic 之上的 BLOB 存储层对外提供全局、可无限扩展的存储底座,并允许用户在持久性与可用性之间进行权衡。

在之前的 @Scale 演讲《Training Llama: A Storage Perspective》中,我们讨论了 Meta 如何通过在 Tectonic 块存储层之上暴露类似 NFS 的文件系统接口,直接在其上训练 Llama。虽然这套架构在 Meta 内部仍被广泛使用,但我们的现代训练栈正在逐步迁移到 BLOB 存储接口之上,整个行业也是如此。这一转变的驱动力来自两方面:一是对 BLOB 存储层中海量数据湖的统一访问需求,二是对高性能的追求。

最大化 GPU 利用率

现代 AI 工作负载"极其消耗数据",并且与传统 Web 应用有着截然不同的特征:突发且持续的高吞吐、可预测且有界的 pMax 延迟、以及多变的 I/O 模式。近年来,BLOB 存储的重点已大幅转向如何最大化 GPU 利用率。

延迟为何重要

要理解低延迟和 pMax 有界延迟的重要性,不妨以模型训练为例。训练过程中,数十万块 GPU 会多次(即跨多个 epoch)迭代存储中的海量数据,并以批(batch)为单位训练数据集。每隔一定的步数或批次,GPU 之间就需要同步状态。只要有一块 GPU 变慢,这个同步步骤就会拖慢所有 GPU,进而拖慢整个训练流程。

图 1 展示了两块 GPU 上的数据加载流水线。每台 GPU 主机上的数据加载器会在 GPU 处理当前批次的同时预取下一批数据,以最大化计算与 I/O 的重叠。在 GPU1 的情况下,存储取数延迟完全在可控范围内,GPU 从不会因等待 I/O 而停滞。而在 GPU2 的情况下,有两处存储取数出现了高延迟,导致 GPU 停滞。这些停顿最终延迟了整个步骤的完成时间。

图 1:两块 GPU 上的数据加载。

传统 BLOB 存储架构无法胜任 AI 需求

多年来,BLOB 存储以典型的服务化方式有机演进,一层一层往上叠加。其中许多层都是带状态的,各自维护着自己的元数据存储。虽然对于使用全局 HDD 的传统场景来说,这些元数据访问的延迟通常算不上瓶颈,但对 AI 工作负载而言却是致命的——它们需要在毫秒级内访问闪存中的数据。图 2 展示了典型 getObject("/bucket/path") API 的请求流程。请求到达 API 服务器后,服务器要在 namelayer、volumeslayer 和 containerlayer 之间做大量元数据查询,才能把路径解析成一组 (blockId, offset, size) 元组。部分查询还可能跨区域,延迟累加起来达到几百毫秒并不少见;只要其中任何一次查询响应慢一点,整个请求就废了。查询完成后,API 服务器再把数据从 Tectonic 层代理转发给客户端。

图 2:旧版 getObject API 请求流程。

这套架构虽然很好地服务了传统工作负载,但当初指导设计权衡的那些基础假设已经发生了改变,主要包括以下几点:

  • 性能与延迟:如前所述,传统工作负载对延迟的要求并不高,而 AI 工作负载则要求从始至终都有可预测、有上限的延迟,一直延伸到 pMax。
  • 可靠性与持久性:老架构的设计目标是在区域级故障下依然保持高持久性和高可用性,数据和元数据默认全局复制。虽然 AI 工作负载同样要求极高的可用性,但"默认全局"这个设计选择已经不再适用了。
  • 成本效率:老架构构建在 HDD 之上,高度优化了单位字节成本。AI 工作负载的 IOPS 需求决定了必须使用闪存,而且存储的计算开销相对于 GPU 的计算开销已经可以忽略不计。
  • 能效:在 GPU 时代,数据中心越来越受电力限制而非空间限制。每消耗在存储上的一千瓦电力,就意味着 GPU 少了一千瓦电力可用。这是 AI 工作负载带来的全新约束。

简而言之,权衡空间已经发生了足够大的变化,我们必须重新思考整个架构。

重建基础

在搭建新基础架构的过程中,我们做出了以下几个关键的设计决策:

  • 统一的元数据 schema:我们重写了元数据子系统,把分散在各层的元数据整合成一套统一的扁平 schema,底层由 ZippyDB 提供支持。这为路径到存储地址的 O(1) 查询打下了基础,是一个质的飞跃。
  • 取消数据面代理:我们去掉了数据面代理,改用一个大而全的客户端 SDK,让它直接从存储服务器把字节流传给客户端。这有助于降低功耗,同时也能获得更高的吞吐和更低的延迟。
  • 区域化部署:BLOB 存储栈现在变得很精简,可以灵活地按区域或全局服务部署。我们目前在各 AI 区域都与 GPU 同机部署一套区域级 BLOB 存储栈。
图 3:getObject API 的新请求流程。

图 3 展示了 getObject("/bucket/path") 的新请求流程。客户端 SDK 收到该 API 调用后,会向 API server 发起 getReadPlan("/bucket/path") 请求。API server 对每个 chunk 在新元数据存储中做一次 O(1) 查询,把路径映射为 (blockId, offset, size) 三元组,然后把 ReadPlanResult 返回给 SDK。SDK 内嵌了 Tectonic BlockClient,因此可以直接从 Tectonic 流式读取这些 block 的数据。经过这些改造,我们重建了基础架构,实现了"在 Tectonic 之上零额外开销"的目标。去掉数据代理之后,功耗预算也得到了有效控制。

应对流量尖峰与热点

在加载数据和 checkpoint 时,AI workload 一个典型特征是会同时跨数百张 GPU 并发访问数据。模型权重等数据子集往往很"热",而 GPU 重启等事件又会引发剧烈的流量尖峰。基础架构的问题解决之后,下一步就是要应对这些尖峰和热点。好在 BLOB 存储层多年来积累了不少应对热点的经验,我们直接把现有方案适配到了 AI 场景。具体来说,我们采用了两种做法:

  • 分布式数据缓存:利用 GPU 主机上的闲置内存,作为高频并发访问数据的分布式数据缓存。为此,复用了 Meta 的 Owl 子系统 中的组件:将 Owl 子系统中的 peer 节点直接集成进 BLOB 存储的客户端 SDK,让所有数据访问都经过该缓存。
  • Readplan 元数据缓存:Readplan 指的是从路径到存储地址的映射。现在,我们将高频访问 BLOB 的 read-plan 缓存在一个类似 memcache 的分布式内存存储中。

实际运行中,分布式数据缓存的平均命中率为 80%,read-plan 缓存的元数据访问延迟为 1–2 毫秒。这些简洁的机制本质上做了三件事:

  • 吸收流量尖峰,降低对存储层的 I/O 需求。
  • 解决元数据热点分片问题。
  • 通过从内存读取来改善 p50 和 p99 延迟。

协议优化

前面讨论的内容让我们解决了 80% 的问题。剩下的 20% 通过定位并修复全链路瓶颈来完成。以下是其中一些值得关注的问题,虽然远非完整列表:

  • 慢节点(Laggards):单个慢速存储节点拉高尾延迟。这是一个众所周知的问题,我们在客户端采用了 hedged reads(对冲读取)来缓解。
  • 出口流量尖峰:在 checkpoint 事件期间,客户端常常会产生剧烈的出口流量尖峰,进而引发拥塞、超时和重试,最终导致 GPU 停顿。我们通过在客户端 SDK 上构建动态并发控制来解决——根据应用层拥塞信号自动调节并行度。

综合以上优化,新的 BLOB 存储栈现在能够在不造成 GPU 停顿的前提下服务 AI 工作负载,并且在 Tectonic 层之上引入的开销可以忽略不计。下一阶段的重心转向了研究效率。

最大化研究效率

GPU 资源稀缺,且越来越呈现出跨地域分布的趋势;同时,训练任务为了性能又要求数据与 GPU 同区域部署。这给研究人员带来了一个有趣的挑战:他们需要自己负责跨区域的数据接入和迁移。

在 Meta,一次典型的训练任务提交流程如下:

  1. 研究人员从各种来源采集数据,对数据进行丰富化处理,并将其持久化存储到 BLOB 存储中。
  2. 研究人员选择一个区域来运行任务。
  3. 研究人员提交一个数据导入任务,将训练数据集的快照以适合从 GPU 宿主机加载的文件格式生成到目标区域。
  4. 研究人员等待数据导入完成;根据数据集大小,这一步可能需要数小时。
  5. 研究人员提交训练任务并监控运行状态。
  6. 研究人员分析输出结果、调整数据集,然后从第 3 步开始再次迭代。

第 2 步到第 4 步可能耗时数小时,直接影响研究人员的迭代速度。理想情况下,我们希望研究人员的时间花在调优模型上,而不是等待存储。目前,研究人员会在启动任务前复制快照,让数据与 GPU 同区域部署,以获得最优性能。虽然这种性能优化对于持续数周乃至数月的大规模训练任务来说是合理的,但绝大多数任务规模要小得多;这些任务的研究人员完全愿意以偶尔的性能下降换取更快的迭代速度。

因此,我们需要一套系统,让研究人员只需导入一次数据,就能在任意位置访问数据,而无需考虑区域边界。我们需要一套工作流,使研究人员能够以分钟级而非小时级的速度进行迭代。当我们重新审视问题时,这些数据集"一次写入、多次读取"的特性让我们灵光一闪:如果我们把存储看作行星规模计算机中的一块磁盘,借鉴操作系统的设计思路会怎样?当 Linux 进程中运行在 CPU 核心上需要从磁盘读取文件时,操作系统会在缓存各层之间按需透明地加载数据——包括内存中的页缓存以及 CPU 的 L2、L1 缓存。这一直觉催生了图 4 所示的架构演进:

图 4:数据加载架构的演进。

核心思路是把各种主机内和主机外的存储资源作为分层缓存来使用,底层是由 HDD 支撑的全局 BLOB 存储作为最终数据源。具体来说,我们把 GPU 主机上的内存和闪存用作 L1 和 L2 缓存,把由闪存支撑的区域级 BLOB 存储用作 L3 缓存,数据加载器继续通过熟悉的 BLOB 存储 SDK 访问存储。为了有效掩盖延迟并简化数据生命周期,我们依赖以下机制:

  • 数据加载器预取:数据加载器在处理当前批次的同时,会把下一批数据集预取到内存中。这个预取在 BLOB 存储 SDK 层面体现为一次读操作。
  • 深度预取:我们在 BLOB 存储 SDK 中暴露了一个显式的 prefetch() API。数据加载器在后台调用 prefetch() API,提前拉取接下来几分钟内需要的数据。该 API 会触发数据从远程存储加载到本区域的 L3 缓存,并预热元数据缓存。
  • 自动数据生命周期:L3 区域级分离式闪存层中的数据通常会保留一段可配置的时长,以便在训练周期的各个 epoch 之间复用。我们支持自定义驱逐策略,包括 TTL 和 LRU 策略,这些策略同样会感知容量和配额。

新数据加载模式上线后迅速被采用,目前我们在生产环境中同时支持新旧两种加载模式。为了用数字说明效果,图 5 给出了上线前后各工作负载的摄取时间对比:

图 5:上线前后的摄取时间。

在前沿模型每隔几周就发布一次的时代,数据加载模式的这一转变是进一步提速所亟需的变革。

要点总结

现代 AI 工作负载对数据需求量巨大,存储在算力成本和创新速度上都扮演着重要角色。存储瓶颈直接影响 GPU 利用率和算力成本,而在 GPU 分布在全球各地的当下,跨区域数据接入所耗费的时间会直接影响研究迭代速度。Meta 的 BLOB 存储架构最初是为支撑 Meta 系应用而构建的,要服务 AI 工作负载,需要在性能上实现跨越式提升。这促使我们重新思考整个架构:通过重建元数据子系统,并采用带预取和按需加载的分层缓存架构,我们得以有效满足当下工作负载的需求。

未来工作

我们持续演进 Meta 的存储能力,以跟上硬件演进和工作负载需求的步伐。该领域的未来工作包括:

  • 将存储扩展到网络极限。
  • 在更大规模下支持 GPU 不停顿的 checkpointing。
  • 应对推理工作负载带来的新挑战,我们正着手攻克。

分享到:

原始来源: Meta 工程博客

评论 (0)