ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI项目存储选型:训练数据、模型权重与向量索引如何分层

AI项目存储选型:训练数据、模型权重与向量索引如何分层

做模型训练的同学应该都有过这种经历:GPU利用率上不去,显卡在等数据,训练脚本的日志里好半天不见一个step更新。一开始大家会去调模型结构、调学习率,折腾了半天才发现,瓶颈不在算力,在数据喂不上来。进入AI时代之后,“数据如何存储”这个问题的复杂度被严重低估了。训练集、模型权重、向量索引、验证集、日志和样本库,每一类数据的体量、读写模式、可靠性要求都不一样,一套传统存储方案包打天下的做法越来越行不通。

这篇文章把我这些年梳理AI项目数据存储方案时沉淀的判断标准写出来,不绕弯子,直接对照使用场景说清楚:什么样的数据该放在哪一层,性能和成本怎么权衡,以及运维阶段最容易忽略的细节。准备搭建训练平台的技术负责人、被数据加载卡住的算法同学,以及刚接触AI基础设施的运维朋友,应该都能从中找到一条能落地的思路。

1. AI负载让传统存储手忙脚乱:先看清三对矛盾

要决定数据放哪,先得搞清楚AI工作负载对存储到底提出了什么要求。传统业务系统的存储负载相对单一,数据库多半是随机小IO,文件服务器多半是顺序大IO,两者分得很开。AI项目则不一样,同一个平台里既有训练作业,又有推理服务,还有一堆数据预处理任务,存储要同时面对几对互相矛盾的需求。如果不先把这些矛盾摊开,后面谈介质、谈协议,很容易变成参数对比,落不到真实场景里。

1.1 顺序读与随机读的冲突

训练作业读数据时,基本以流式顺序读为主,一次要读出几百MB甚至几个GB的特征文件送进GPU流水线,希望存储能持续提供高带宽。推理服务则相反,向量检索、特征召回这类操作每次只读几KB,但并发量极高,要求单个请求的时延在毫秒级。说白了,前者像开着重卡走高速想要宽路,后者像大量小轿车排队出停车场,想要多个出口、快速放行。用同一套存储去扛这两种负载,高峰期必然互相拖累。

这类冲突在真实环境里非常普遍,也很容易在运维监控图上看到:训练任务一启动,共享存储的带宽被打满,在线推理的延迟立刻恶化;数据加载一停,存储又大量空闲。如果平台里训练和推理并存,我建议至少在存储层面把这两类流量分开,或者给不同业务设置明确的带宽上限。否则后续的很多优化都会被这层互相干扰拖住。

1.2 海量小文件与大文件的体积差

第二对矛盾是数据预处理阶段产生的海量小文件。语料切片、图像裁剪、音频分段之后,经常出现几百万个KB级别的小文件。很多团队把数据集原样丢进文件系统,结果还没开始训练,光遍历目录就能等半天。问题本质不在总容量,而在元数据。一个小文件对应一条元数据,百万小文件就是百万条元数据,文件系统的inode、目录索引都会不堪重负。

同样是1GB的数据,拆成100万个小文件和合并成1000个1MB级别的块文件,访问效率会差一个数量级。所以数据入库前先做“大文件化”是性价比最高的优化,把原始样本打包成分块格式,让存储只面对少量大对象,元数据开销降下来,训练读取速度也能提上去。这一点常被人忽视,却是很多性能问题的第一根源。

1.3 从TB到PB的架构换挡

第三对矛盾很容易被低估:数据规模从TB跨到PB,不是容量不够再买几块硬盘那么简单。TB级阶段,一台大容量服务器加本地高速盘就能覆盖很多场景,部署简单、运维成本低。到了PB级,单机的带宽上限和存储密度很快就到顶了,网络带宽、数据分片、多存储池、数据调度这些问题都会冒出来。可以类比搬家,十个箱子叫一辆大车就行,一千个箱子需要的是车队,还要考虑装车顺序、路线和仓库位置。

AI平台的数据量往往增长得非常快,几次新数据集导入、模型迭代和历史归档叠加在一起,容量就会突然跃迁。如果架构一开始没有留出横向扩展的接口,后面做数据迁移会非常痛苦。规划存储时最好先问一句:这个平台半年后数据量会是多少?答案往往直接决定要不要上分布式方案。

2. 按数据角色安置数据:训练集、权重、向量索引该放哪

很多人一想到存储方案,第一反应是先挑工具、看参数。我更建议反过来,先把平台里的数据按角色拆开:它们分别是谁在什么场景下访问的,丢失会造成什么后果,访问频率有多高。角色分清楚了,落在哪一层几乎是自然得出的结论,而不是靠拍脑袋选一个昂贵的“通用方案”。

2.1 训练数据:吞吐优先,冷热分层

训练数据集通常由一组大块文件组成,特征是体积大、顺序读取为主、丢失后可以靠原始数据重新生成。相比“绝对不能丢”,它更在意“读取时不要拖慢GPU”。针对这种角色,比较合理的做法是两层结构:原始数据放在容量型存储里,比如对象存储或大容量低频池,它们便宜、容量大;训练当前需要的子集则提前放到高速层,比如本地的SSD或者共享高速存储,让训练作业读取时带宽充足。

实际操作中我建议把数据管线设计成“先预热、再训练”。启动训练任务前,先从容量层把本epoch要用的数据拉到高速层,或者用并行预取的方式提前填满本地缓存,再开始跑计算。如果让训练边跑边从冷存储层拉数据,一来带宽波动会影响训练稳定性,二来冷存储层扛不住高并发,很容易成为新的瓶颈。很多训练框架都有自动的预取机制,但前提是底层存储能提供足够的突发带宽,这个提前量和硬件能力要在压测阶段就摸清楚。

2.2 模型权重与检查点:可靠性优先,快照思维

模型权重和检查点是另一类角色:单个文件可以到几百GB,写入频率不高,但可靠性要求极高。训练跑了两天的checkpoint如果损坏或丢失,前面计算基本白费。所以它们不该和训练数据混在同一个优先追求吞吐的池子里,而应该放进一个写一致性更好、有明确版本管理的存储区域。

这里有一个容易被忽视的细节:保存检查点时要避免直接覆盖旧文件,而是要写出一个完整文件后再原子切换引用。很多框架原生支持这类写法,但工程上仍然常看到半写状态的文件导致恢复失败。另外一个实用建议是设置多个保存点,比如保留最近三轮的检查点,再配合一个稳定的命名指针指向最新版本。恢复程序只要读最新的指针,不必在几十个文件名里猜哪个才是可用的。

2.3 向量索引与特征数据:低时延优先,内存和磁盘协同

向量索引的角色又不一样。推理服务每次查询要快速找出与输入相近的topK向量,请求规模可能是每秒数千次,读的是KB级别的小对象,对时延极其敏感。把索引全部放内存当然最快,但千万级向量构建出的索引往往有几十GB甚至上百GB,内存装不下或者太贵。更常见的做法是分层:热点子集留在内存或本地高速缓存,完整索引放在SSD存储上,查询引擎负责在内存未命中时快速从磁盘加载对应分片。

还需要注意索引构建和查询的资源隔离。索引构建是计算和IO密集型任务,如果和在线查询共用同一批存储节点,构建期间极有可能把查询时延打爆。我建议把构建任务放在单独的执行周期,或者用资源组把两者的IO限流参数分开。服务启动时的索引加载也得提前设计,让服务预热完成后再对外接收流量,否则新节点加入时就成了全集群的存储IO冲击点。

3. 介质、协议与分层参考架构:存储选型的三张表

把数据角色分清楚之后,接下来要解决的是技术选型。存储系统通常可以从三个维度去看:底层介质、访问协议、整体架构。每个维度都有自己的适用边界,用错了场景,再贵的设备也发挥不出价值。介质决定了物理性能的上限,协议决定了数据能被谁访问,架构决定了容量和扩展能力。三个维度分开考虑,再组合起来看,才不会在具体产品的参数里迷路。

3.1 介质层怎么选:硬盘、固态、内存的主场

介质优点短板适合的AI数据
机械硬盘容量大、单位成本低随机IO差、延迟高原始数据集、归档数据、低频备份
固态硬盘随机和顺序IO都优秀单位成本高于HDD活跃训练集、活跃检查点、向量索引
内存微秒级延迟、极高IOPS容量小、断电丢失向量热索引、频繁访问的样本缓存

介质之间不是替代关系,而是分层关系。大容量HDD负责把所有数据装下来,SSD承担活跃数据的读取,内存缓存兜住最热门的访问。预算充足当然可以全SSD,但全闪方案对海量冷数据并不划算,到了PB级阶段,容量和成本会非常难看。实际上很多AI平台跑得稳,靠的并不是某一层特别豪华,而是让每一层都干自己擅长的事。

3.2 访问协议怎么定:块、文件、对象、键值的边界

访问协议决定了数据怎么被读写,也决定了能同时服务多少客户端。

协议/形态典型使用方式优点不适合
块存储单机挂载,本地盘延迟低、可靠多机共享困难
文件系统(共享)多台机器挂载同一个目录读写方便、适合训练集共享和代码元数据压力大、高频小文件场景容易吃力
对象存储HTTP接口读写大对象容量大、元数据丰富、适合海量数据高频小对象改写效率低
键值/缓存按key访问小对象毫秒级延迟容量有限,不适合大文件

选协议关键看使用方式:训练作业希望多台机器同时读一批数据,共享文件系统或对象存储的流式读取比较合适;数据库这类低延迟随机写场景,块存储更稳;推理特征缓存和向量热索引,键值缓存是主流。很多时候协议选错,不是性能问题,而是架构问题。比如向量索引这种类型,不太适合硬塞进传统关系库,也不是所有键值缓存都能扛住近邻检索的计算量,需要在专用的检索引擎和底层存储之间做好分工。

3.3 一套小型AI平台的分层落地参考

如果没有特别复杂的背景,我建议直接按下面这个五层结构起步。它不追求极致的硬件表现,但足够支撑绝大多数训练和推理任务。

层内容说明
GPU服务器本地盘当前模型的临时文件、推理加载缓存走最快路径,掉电可重建
内存/缓存层向量热索引、频繁读的小样本毫秒级响应,容量不用贪大
高速共享存储当前活跃的训练集、最近检查点SSD介质,多机可共享
容量型共享存储全量训练集、历史检查点HDD/对象存储,追求容量和成本
归档/低频池原始数据集、旧版本模型极少访问,可接受较高恢复时延

这个结构把数据按热度放进了不同的容量和速度区间。训练时,数据从容量层或高速层读取;推理时,向量子集从内存和缓存响应;归档时,旧数据安静地待在低频池里。每层之间通过策略自动迁移,就形成了一个完整的数据流转闭环。它也是后面生命周期管理的基础。

4. 性能、容量与成本怎么平衡:算好三笔账

方案设计得再漂亮,最后都要过预算这一关。性能、容量、成本三者之间没有绝对最优,只有针对业务场景的相对最优。同一块存储,对训练任务可能贵得离谱,对归档数据又不够便宜。我的做法是不纠结于厂商宣传的单项指标,而是把整个问题拆成三笔账来算:带宽账、容量账、单位成本账。每笔账对应一类数据角色,算清楚了,选型思路自然就清晰了。

4.1 峰值吞吐和平均IOPS,别被标称值骗了

第一笔账是带宽账。很多人选存储只看协议带宽标称值,比如网络号称10GbE,就以为能跑满1.25GB/s。实际读小文件或高并发时,有效吞吐常常只有标称的五六成。训练场景要关注的不是瞬间峰值,而是持续加载过程中的稳定带宽;推理场景要关注的不是平均时延,而是P99这类长尾分位数。简单说,训练看“能不能一直保持高吞吐”,推理看“最差的那些请求是不是还在可接受范围”。

一个常见的坑是批处理和在线服务共用同一存储池:离线导出数据时把带宽吃满,在线查询的P99立刻变差。解决办法要么是分池隔离,要么是给不同业务设置带宽上限。如果共存无法避免,至少要在监控里区分带宽使用来源,出了问题能快速定位是哪类任务在抢资源。

4.2 压缩、去重、量化:从源头降低存储体积

第二笔账是容量账。与其不停扩容,不如先给数据瘦身。文本类语料压缩率通常很高,几十GB的原始文本压缩后可能只剩三分之一;重复样本在数据集中比想象中多,尤其是爬取类数据,做个去重不仅能省容量,还能缩短训练时间。模型权重也有明显的压缩空间,FP32转成BF16或INT8,体积可以减半甚至更多,不过要提前做精度测试,别等训练完才发现效果不达标。

这里有个容易被低估的副作用:压缩和解压会消耗CPU。如果训练流水线每次读取都要实时解压,CPU反而可能成为新瓶颈。所以更合理的做法是在数据入库阶段就做压缩和格式转换,读取时尽量选择不需要现场解压的格式,或者用硬件和预解压缓存把开销挡在训练关键路径之外。

4.3 用单位成本倒推选型,而不是先看品牌

第三笔账是单位成本账。不同数据角色对应的最优成本指标不一样:存储大量冷数据,关心的是每GB成本;训练数据频繁读取,关心的是每GB每秒带宽成本;在线推理访问的小对象,关心的是每百万次查询成本。把预算按这个口径拆开,会发现很多选型问题根本不纠结。

我通常会画这样一个示意表来和团队对齐预期,数字按实际环境填:

数据角色关心指标常见量级参考
原始数据/归档每GB成本低到中等即可
训练数据每GB/s带宽成本优先保证带宽
向量索引热层每百万次查询成本优先保证时延
检查点/快照每TB可靠容量成本可靠性和容量并重

另外提醒一句,容量规划不要卡着上限买。索引副本、临时文件、日志、回收站,这些看不见的数据很容易占到实际容量的20%以上。我习惯在预期需求基础上额外预留25%到30%的余量,这部分看起来浪费,但在数据突增时能救急。

5. 数据生命周期与运维管理:存储买完只是开始

存储不是建设完就一劳永逸,数据会一直增长、变化、过期。很多平台上线时跑得不错,半年后却越来越慢,问题不在于当初选的设备差,而是数据没有按生命周期被妥善管理。真正拉开差距的往往是运维阶段做得细不细:自动分层策略是否合理,备份是否真的能恢复,告警是否设置对了指标。我更愿意把存储运维当成一项长期投入来看,下面几个环节,是从实际故障里总结出来的。

5.1 自动分层策略怎么定才不出乱子

自动分层可以简单理解为给数据定生命周期规则。通常按最后访问时间和访问频次两个维度来划分:当前训练和推理要用的数据留在高速层,超过一段时间没被访问的数据自动降到容量层,再长时间没动的进入归档池。具体时间窗口要根据任务节奏来定,比如训练任务每周一轮,就可以把“7天未访问”作为降级条件;如果任务每天跑好几次,窗口适当缩短。

这个策略看起来简单,真正实施时容易出两个问题。一是批量迁移任务会在某个时间点集中触发,把网络带宽和存储IO占满,导致在线服务抖动。解决方法是给迁移任务限速,并把迁移窗口安排在业务低峰期。二是自动化策略有可能误伤正在使用的数据,比如某个训练任务用旧数据集做验证,这段时间数据没被读取但之后又要用,就容易被当作冷数据降级。稳妥的做法是加白名单,让核心业务数据不参与自动降级,或者设一个更保守的检查周期。

5.2 多副本不等于备份:恢复演练才是底线

存储设备上的多副本、纠删码只是防硬件故障。如果遇到误删、程序bug批量更新、恶意破坏,所有副本上的数据会被同步改掉,这个时候副本越多反而越“同步丢失”。所以备份和副本是两回事。对AI平台来说,至少要保证关键数据(原始数据集、模型检查点、重要的中间结果)有独立于主存储的备份,并且对备份本身做不可变保护,防止被覆盖或篡改。

比备份更常被忽略的是恢复演练。存档放在那儿,半年没碰过,真出事需要恢复时才知道备份链路早就坏了。我建议至少每个季度挑一个样本:从一个随机的检查点或小规模数据集开始,完整走一遍恢复流程,记录恢复时间和失败点。跑过一次恢复演练之后,你对整个存储体系的信任程度才会真正建立起来。

5.3 盯住几个关键监控指标,提前发现隐患

存储监控不用贪多,关键指标盯住几个就够用。

  • GPU等待率:训练任务中GPU等待数据的比例,一旦持续偏高,优先怀疑数据读取链路。
  • 存储带宽利用率:训练数据加载期间带宽是否持续接近上限,长期高于80%要考虑扩容或分层。
  • 时延分位数:在线推理读写场景看P99,不要只看平均值。
  • 容量水位与增长率:规划是否跟得上数据增长,建议在预警线以下较早介入。
  • 元数据与文件数量:小文件数量膨胀是性能恶化的前兆。

告警阈值也别拍脑袋定。我见过不少系统设置“平均时延超过100ms就告警”,结果存储偶尔慢几秒也没人发现。相反,P99分位数和白天的带宽峰值更容易暴露真实风险。监控的价值不是收到一堆通知,而是能在用户感受到故障之前定位到可疑环节。把监控项和上面的数据资产清单对应起来,更换存储层、调整分层策略时,也能更早发现影响。

6. 落地节奏与团队协作:方案再好也要人用起来

存储选型里有一个大家不太愿意明说的现实:一大半失败的方案,不是技术选型错了,而是在地执行时断了链子。同样是训练平台,有没有人负责数据分层,有没有人在训练前做预热,有没有人定期恢复备份,长期下来差距会非常大。最后这部分想聊点工程和管理层面的东西,技术指标只是一方面,团队怎么协作、上线节奏怎么安排,同样决定这套方案能不能持续跑下去。

6.1 别让算法团队和运维团队各管一段

存储选型看似是基础设施团队的事,实际上和算法工程紧密绑定。不同框架读取数据的方式不一样,有些走共享文件系统,有些走对象存储接口,有些依赖缓存预取;不同推理服务对时延和可用性的要求也差异很大。如果算法团队只负责模型、运维团队只负责硬件,两个环节脱节,就会出现买了很贵的高速存储但训练脚本仍然没吃满带宽的情况。

比较好的协作方式是在项目初期就一起过一遍数据流:算法同学说清楚读什么东西、并发多少、连贯性要求如何;运维同学说清楚可用预算、各层容量和带宽上限。把这两份信息叠加,存储架构基本就定了。不要到采购完成之后才反过来要求存储适配算法,那是成本最高的一条路。

6.2 小团队的低成本起步路径

对大部分团队来说,一上来就搭完整的分布式存储集群未必划算,运维成本可能比硬件成本更贵。更务实的路径是按数据量分阶段走。

数据量级推荐思路核心关注点
10TB以内本地大容量盘 + 外部备份简单可靠,先跑通业务
10TB-50TB容量型服务器 + 缓存层训练数据预取和带宽规划
50TB-100TB以上分布式存储或对象存储底座数据调度、分层策略、容量治理

小团队容易犯的错是想一步到位,结果花大量精力维护一个当前根本用不上的复杂系统。数据量没到那个量级之前,分布式存储带来的扩展性优势,远不如一台大容量服务器加良好备份来得省心。真的到了需要扩展的时候,再按数据角色分批迁移,风险要小得多。

6.3 几个值得提前避开的坑

结合这些年看到的线上问题,我列几个共性比较高的坑,每一个都对应着真实的故障成本。

  • 用传统关系数据库存向量数据:数据量小还能接受,量一大,无论是写入还是近邻检索都会非常吃力。
  • 检查点只存一份:不管主存储多可靠,至少要在独立位置再放一份,否则一次误删就能让训练进度归零。
  • 训练任务直接读写归档层:归档层设计目标不是高带宽,边训练边读归档,基本等于主动制造瓶颈。
  • 忽略小文件优化:百万级小文件不是容量问题,而是元数据问题,入库前做大文件化处理能省掉大量后续麻烦。
  • 上线前不做全链路压测:存储问题往往在并发上来后才会暴露,压测至少要把数据读取、写入、备份、恢复四类场景各跑一遍。

我的习惯是给每一类数据都整理一张简单的卡片,写清楚容量、访问模式、峰值带宽、容忍时延、保留周期这几项,然后放到团队文档里。后面讨论架构、采购、调整分层策略时,所有争论都围绕这些数字展开,而不是凭感觉打嘴仗。这个习惯看着笨,但它能帮助团队把存储问题讲清楚,也就解决了一大半。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进