0

大模型基石 AI 分布式存储工程实战

胜多负少
4天前 7

获课:xingkeit.top/16497/


大模型 RAG 底层基建:AI 分布式存储工程实战项目复盘

在大模型应用如火如荼的今天,RAG架构已成为企业接入私域知识的标配。然而,当业务从原型验证走向生产落地时,一个底层问题逐渐浮出水面——存储基建跟不上上层应用的膨胀速度。向量数据从百万级飙升到亿级,文档切片从日增几百变成日增几十万,Embedding计算、索引构建、查询吞吐每一环都在挑战传统存储架构的极限。本文基于一个真实的企业级RAG分布式存储项目,从工程视角复盘设计决策、踩坑经历与优化路径,聚焦架构思路与实战教训,为同样走在“基建补课”路上的团队提供一份可参照的地图。

项目背景与规模画像

该项目服务于一家大型企业的内部知识问答系统,覆盖技术文档、产品手册、客服语料、合规文件等十余个来源。项目启动六个月后,数据规模达到:原始文档约15TB,文档切片后文本块数量突破2亿条,对应的Embedding向量(1536维float)占用存储约1.2TB,加上元数据、全文索引和日志,总存储量接近20TB。查询端日均请求约50万次,P99延迟要求控制在1.5秒以内。

早期原型阶段使用单机PostgreSQL加pgvector插件承载一切,但在向量数据超过5000万条后,查询延迟开始急剧恶化,索引构建时间从小时级变成天级,VACUUM操作甚至无法在维护窗口内完成。这正是启动分布式存储重构的导火索。

架构选型:向量数据库的战场

面对海量向量数据,第一个需要回答的问题是:采用专用的向量数据库,还是在通用分布式数据库上叠加向量能力?项目组经过两轮选型对比,最终确定了混合架构——以Milvus为核心向量引擎处理Embedding的存储与检索,以对象存储+数据湖格式管理原始文档和切片。

选择Milvus而非在Cassandra或TiDB上加装向量插件的核心理由有三:一是Milvus原生支持GPU加速索引构建(IVF_FLAT、HNSW等),亿级数据的全量索引可以从四天压缩到六小时;二是其对标量过滤与向量检索混合查询的优化(先过滤再检索或反之)更成熟;三是团队实际压测显示,同等数据量下Milvus的QPS约为pgvector的5-8倍。

代价也是存在的。Milvus的运维复杂度明显更高——依赖MinIO做对象存储、依赖Pulsar处理写入日志、依赖Etcd做元数据协调,组件众多导致故障排查链路长。项目初期就遇到过Pulsar消息积压导致的数据一致性延迟问题,花了整整一周才定位到是生产者的批处理配置不当。

数据接入管道:从文档到向量的全链路优化

分布式存储不是孤立的系统,而是嵌入在整个数据处理管道中的一环。项目重构过程中最大的一笔“技术债”,是早期用Python脚本串行处理文档入库,不仅慢而且无法断点续传。

重构后的管道采用了经典的数据湖三阶段架构:

原始区(Raw Zone):文档(PDF、Word、Markdown)原样落入对象存储,路径按/source/{source_name}/dt={yyyyMMdd}/组织。这一层只做完整性校验,不做任何清洗,保证数据可回溯。

处理区(Processed Zone):Spark作业从原始区读取文档,调用分布式解析服务(部署在K8s上的文档解析容器集群)提取文本,再按语义边界切块。切块后的文本加上来源信息、时间戳、哈希指纹,写入Iceberg表。这一步的关键设计是幂等性——同一个文档重复运行时不会产生重复切片,通过文档哈希与分区路径联合去重实现。

向量区(Vector Zone):另一个Spark作业从处理区读取切片,调用Embedding服务(GPU集群上部署的Sentence-Transformer批处理接口)生成向量,将向量连同切片ID、标量字段一起写入Milvus。这一步的性能瓶颈在网络和GPU推理,优化后采取动态batch(根据GPU显存利用率自动调节每批切片数),吞吐量提升了3倍。

踩坑实录:分布式环境下的五个教训

理论设计与生产运行之间的距离,往往由一个个具体的坑来填补。以下五个教训耗费了项目组最多的排查时间:

教训一:向量索引的内存占用远超预期。Milvus的HNSW索引在构建时会将所有向量加载到内存。项目在构建1.5亿条1536维向量的索引时,仅内存需求就达到约120GB,超过了单个查询节点的配置。最终方案是切换到磁盘索引DiskANN(Milvus 2.3后支持),牺牲少量查询延迟换取了内存压力的释放。

教训二:对象存储的请求限流。Spark作业在读取原始区时,大量并发的ListObjects和GetObject请求触发了对象存储服务端限流(特别是使用MinIO集群时)。解决方案是在Spark读取器中增加可控的请求延迟,并启用批量预读,将小文件请求合并为大范围读取。

教训三:时间戳陷阱导致的数据新鲜度问题。Milvus默认的索引构建策略是写入即可见,但对于批量导入场景,持续写入会导致索引碎片化严重,查询性能随时间下降。修改策略为定时触发索引构建(每15分钟),牺牲几分钟的数据延迟,换取查询性能的稳定。

教训四:事务边界的错位。文档处理和向量生成两个阶段如果分开提交,当某批次向量写入失败时会留下“有切片无向量”的孤儿记录。解决方案是在处理区维护一个向量生成状态字段,后台修复作业定期扫描未处理记录重试,同时保证查询端过滤掉未完成向量化的切片。

教训五:冷热数据分层缺失。三个月前的文档被查询的概率不到5%,却占据同等存储和索引资源。在Milvus中按时间分区(按周分区),通过查询路由将过期分区自动路由到性能较低的HDD节点,同时从热索引中移除。这一改造释放了约60%的热存储成本。

查询层的工程化:从能查到查得快

存储基建的最终价值体现在查询性能上。项目在查询层做了三个关键优化:

多级缓存体系。L1缓存是Caffeine本地缓存,缓存高频查询向量及其结果(TTL 5分钟);L2缓存是Redis集群,缓存用户问答对,命中时直接返回历史答案而不触发检索;L3是Milvul的结果缓存(查询去重)。三层缓存使P99延迟从850ms降至210ms。

查询改写与路由。基于历史查询日志训练了一个轻量级分类器,将查询分为“精确匹配类”(如“合同编号XXX的条款”)、“语义泛化类”(如“试用期离职流程”)和“混合类”。精确匹配类走关键词倒排索引+向量重排,语义类走纯向量检索,混合类走并行查询后融合。分类路由使无效召回减少了约35%。

级联重排策略。Milvus初召回1000条候选,通过一个轻量级Cross-Encoder模型重排取Top 20,最后送入大模型。初召回阶段追求高召回率(允许一定噪声),重排阶段追求高精度。这一两级漏斗既控制了大模型上下文的长度,又保证了答案质量。

运维与可观测性:看不见的基石

分布式存储系统的运维复杂度远高于单体数据库。项目组被迫建立了一套针对向量存储的专属可观测体系:

  • 指标层:采集Milvus各节点的CPU/内存/磁盘使用率、向量检索的QPS和延迟分位数、索引构建任务队列长度、Pulsar消息积压数。

  • 日志层:向量数据库组件日志统一采集到ELK,设置告警规则——如“HNSW索引构建失败”“Pulsar backlog超过10万条”。

  • 链路追踪层:为每一个查询请求生成Trace ID,贯穿应用层→Milvus代理→查询节点→对象存储的全过程,定位慢查询时可以在Jaeger上看到每个子环节的耗时分布。

一个辅助提效的工具是自研的“数据健康巡检器”,每日凌晨扫描系统状态:检查是否有切片未生成向量、检查各时间分区的索引是否完整、检查向量与原始文档的可追溯链是否断裂。这个巡检器在上线第一个月就发现了两起因配置变更导致的写入丢失问题,避免了数据静默损坏。

复盘总结:分布式存储并非银弹

回顾整个项目,最大的认知转变是:分布式存储解决的是“规模问题”,但同时引入了“复杂度问题”。对于数据量尚在百万级以下、增长速度不快的团队,pgvector或Qdrant的单机模式完全够用,过早分布式化反而会陷入组件运维的泥潭。

决策点在于数据规模的增长曲线。当向量数据达到两千万以上,或者日增超过50万条时,分布式架构才变得必要。即便在这个阈值上,也应该优先考虑云托管的向量数据库服务(如Zilliz Cloud),将运维复杂度外包,而非自建全套组件。

对于已经走在自建道路上的团队,本文复盘的五个教训和三层优化可供参照。RAG的底层存储基建远不如上层应用光鲜,但它决定了整个系统的天花板高度。在这片“脏活累活”的领域里,扎实的工程实践往往比炫酷的模型更能拉开差距——毕竟,当大模型等你喂数据的时候,没有谁希望你回答“索引还在构建中,请稍后再试”。



本站不存储任何实质资源,该帖为网盘用户发布的网盘链接介绍帖,本文内所有链接指向的云盘网盘资源,其版权归版权方所有!其实际管理权为帖子发布者所有,本站无法操作相关资源。如您认为本站任何介绍帖侵犯了您的合法版权,请发送邮件 [email protected] 进行投诉,我们将在确认本文链接指向的资源存在侵权后,立即删除相关介绍帖子!
最新回复 (0)

    暂无评论

请先登录后发表评论!

返回
请先登录后发表评论!