资讯动态

Spring AI与向量数据库实战:原理、选型及避坑指南

发布时间:2026/10/15 4:34:32 来源:尧图企业网站定制
很多人第一次接触 Spring AI注意力基本都放在 ChatClient 怎么调、提示词怎么写的上面装个模型、写个 Controller 就能和 LLM 聊起来确实很爽。可一旦想做个像样的应用比如让 AI 基于私域知识库回答问题、做长期记忆、甚至做数据分析问题马上就来大模型记不住那么多上下文每次调用都塞一堆历史记录也不现实。这时候你会反复听到一个词——向量数据库。这篇笔记就专门聊它。我会从 Spring AI 官方设计的角度把向量数据库到底解决什么问题、核心原理是什么、主流选型怎么比较、以及在实际项目里怎么接、怎么避坑完整串一遍。适合已经上手过 Spring AI 基础调用、打算往 RAG 或者智能体方向深挖的开发者也适合正在做技术选型的同学。这篇文章不是官方文档翻译是按我自己踩过坑、跑过测试的经验来写的尽量不绕弯子。1. 先搞明白一件事向量数据库解决的是“记不住”的问题1.1 chat 模型的样子决定了它天生“健忘”所有大语言模型本质上都是“一次性的函数”你给它输入文本它预测下一个 token再预测下一个最后给你一段回答。它不会因为你的问题主动去翻你之前的对话更不会知道你的私有文档里写了什么。ChatClient 之所以看起来有记忆是因为 Spring AI 在背后把历史消息拼进 prompt 一起发给了模型——模型只是被“念了一遍”这些上下文而已。这就带来两个硬约束第一模型输入的 token 数量是有限的哪怕上下文窗口再大也不可能把几百万字的文档都塞进去。第二token 是要花钱、要花时间的每次请求都全量携带成本和延迟都会失控。所以真正能长期保存知识、又能让模型“想起来”的东西不能靠模型本身得靠外部存储。这个外部存储不仅要能存数据还要能按“意思”检索。传统关系型数据库存的是结构化数据like 模糊查询只能匹配字符串根本理解不了“颈椎病有哪些康复训练”和“脖子疼应该做哪些锻炼”是相近的问题。这就要靠向量化加相似度检索来解决。1.2 向量化与相似度检索一句话说透一条文本通过嵌入模型Embedding Model转换后会变成一个几百上千维的浮点数组也就是向量。这个向量的设计目标是语义相近的文本转换出来的向量在空间里离得近语义无关的离得远。判断距离的手段有很多常见的是余弦相似度、欧氏距离、点积相似度数值算出来之后按倒序排列就能拿到“最相似的Top K段文本”。整个流程看起来有点像一个仓库管理员嵌入模型负责把每本书提炼成一串“语义指纹”向量数据库只管按指纹之间的远近把书排序。RAG检索增强生成的套路本质上就是先按用户问题去找 Top K 相关资料再把资料拼进 prompt 喂给模型让模型回答“有依据”。Spring AI 把这条链路抽象成了几个接口EmbeddingModel 负责把文本转成向量VectorStore 负责存取向量和执行相似度检索整个能力直接集成在 ChatClient 的 question-answering 流程里。2. Spring AI 的向量抽象与整体设计思路2.1 VectorStore 接口屏蔽掉后端的差异Spring AI 最讨喜的一点是它不强制你非得用某个具体的向量库。你在代码里面向的是org.springframework.ai.vectorstore.VectorStore这个接口核心方法就三个add写入、delete删除和similaritySearch相似性检索。至于底层是开源的嵌入型库、独立的数据库服务、还是云厂商托管的向量服务都是实现类的事。这种设计最大的好处是业务代码可以做到相对稳定。某天你想从本地嵌入式库换成独立的数据库产品只要调整装配原先的写入和检索逻辑基本不用重写。对中小团队来说这个抽象不自找麻烦反而减少了迁移负担。接口简单不代表底层细节简单。add 方法要做的其实是一连串动作先调 EmbeddingModel 把文本向量化再把向量和原始文本、元数据一起写进后端存储。similaritySearch 则是反过来先把查询语句向量化然后带着向量去后端做 ANN近似最近邻检索最后把命中的文档连同相似度分数一起返回。这些细节 Spring AI 都封装好了但你要懂背后的开销都发生在哪里后面排查慢查询时心里才有数。2.2 EmbeddingModel向量质量的决定性因素向量检索的起步动作是文本向量化。Spring AI 我用了好几种嵌入模型做实测简单总结一下各自的特性嵌入模型维度适合场景备注OpenAI text-embedding-3-small1536通用知识库、成本敏感场景API 收费调用有网络依赖OpenAI text-embedding-3-large3072对精度要求高的场景维度高存储和计算成本都高本地开源模型ONNX 转换版384 ~ 768离线环境、数据敏感场景需要额外下载模型文件做推理优化云厂商自研嵌入模型1024~4096 不等与云生态深度绑定的场景调用方式各异依赖厂商 SDK我个人的建议是如果项目允许联网、预算也够直接用 OpenAI 系嵌入模型精度和稳定性综合表现最好如果数据不能出厂或团队想省调用费用就选本地 ONNX 版模型用 CPU 跑也能接受因为嵌入计算的并发量一般远低于对话接口的并发量。另外务必注意生产环境使用的嵌入模型不要三天两头换。向量本身和模型强绑定换模型后以前入库的向量和新查询向量之间的可比性大打折扣会出现查不准的情况。2.3 RAG 链路里向量存储在哪个位置可以这样理解整体链路文档先被切分Splitter然后对每一段文本调用 EmbeddingModel 转成向量连同原文和文档属性一起写入 VectorStore这是“入库”阶段用户提问时问题文本先向量化去 VectorStore 检索 Top K 相关片段再把这些片段作为上下文拼到 prompt 中发给 chat 模型。我画过一条线来形容 VectorStore 的作用它是模型和私有知识之间的一座桥。没有这座桥模型就只是个只能聊废话的对话机器有了它模型才能回答出业务相关的细节。顺带一提切分粒度会对检索质量产生明显影响。太粗单个片段的语义混杂检索命中噪声大太细片段上下文缺失模型看完之后还是理解不了。按我的经验一般文档用 500800 字左右一段并叠加少量重叠比较稳妥代码或结构化文档再单独调。Spring AI 的 TokenTextSplitter 和自定义 Splitter 都值得仔细看一下。3. 主流向量数据库选型对比3.1 四类选手各自解决不同的问题市面上标的“向量数据库”标签的产品很多但本质上是四类不同的技术路线我在选型时通常按它们这样分类第一类是独立型向量数据库代表作是 Milvus、Qdrant、Weaviate。它们是专门为向量检索设计的存储引擎支持分布式、海量向量、丰富过滤条件、混合检索。适合数据量级很大、对性能和扩展性有硬性要求的线上系统。第二类是嵌入式向量库以 Chroma、LanceDB 为代表。它们以库的形式嵌入到应用进程里零依赖、启动快特别适合原型验证、本地开发、小规模单机部署。第三类是关系型数据库或搜索引擎的向量扩展主要有 PostgreSQL 的 pgvector、Redis 的 Search 模块、Elasticsearch 的 dense_vector。它们的好处是团队不用引入新基础设施直接在现有的存储上多一个能力。数据量中等、团队不想维护多套数据库时很划算。第四类是云托管向量服务比如各大云厂商提供的向量检索服务。省运维集成方便但数据出网合规要仔细考虑成本也要盯紧。3.2 我给中小项目选型时的三条建议先说结论再说理由。如果项目处于 POC 阶段我会直接选 PostgreSQL 加 pgvector如果数据量明确会超过千万级再换到 Milvus如果要求够轻、够简单本地开发先用 Chroma 顶着。理由很简单中小项目最稀缺的资源是团队精力少一个组件就少一份运维负担。pgvector 直接住在现有数据库里备份、监控、权限一脉相承几百个向量和几百万个向量它都能扛住。只有当单表数据量真的涨到对查询耗时敏感、或者要上多副本集群时再考虑专用向量数据库否则收益撑不起复杂度。Spring AI 官方对几种主流方案都提供了 starter 或自动配置用起来都不算麻烦。我列一份实际接入时的对比感受方案接入复杂度运维成本数据量上限感受备注pgvector低项目内加依赖和建表即可低千万级以下够用与业务数据同库备份简单Chroma极低纯本地文件极低百万级以下原型验证效率高Milvus中需要单独部署服务高组件较多亿级向量场景稳定性依赖部署调优Redis 向量检索低已有 Redis 则几乎零成本低百万级以下适合缓存和实时场景云托管服务低按 API 接入无取决于服务规格数据出网需评估3.3 选型时容易被忽略的三个点第一个点是相似度算法的一致性。向量入库时算不算归一化、用的是余弦还是内积要全局统一。后端存储默认的相似度算法如果不一致会导致检索效果不稳定。比如 pgvector 同时支持余弦距离、欧氏距离和内积Spring AI 配置里要显式指定别用默认值糊弄过去。第二个点是索引类型的选择。pgvector 通常用 HNSW 或 IVFFlat前者构建慢一点但查询快后者构建快但数据量变化后查询质量容易退化。很多人只建表忘了建索引结果几十万条向量之后检索速度直线下滑。上了一定规模以后索引和迁移策略同等重要。第三个点是元数据过滤能力。实际业务里用户经常要求“只要某个分类下的相似内容”或者“排除某类来源的数据”。如果数据库不支持在向量检索的同时做结构性过滤就只能捞回来再过滤不仅慢还浪费 token。选型时要把元数据过滤能力当成一等需求来评估。4. 实战在 Spring AI 里接一个向量检索服务4.1 准备阶段本地起一个 PostgreSQL 并启用向量插件这部分我以 pgvector 为例因为它最不挑环境也最接近大多数团队已有的技术栈。第一步是装 PostgreSQL版本尽量 11 以上然后执行CREATE EXTENSION vector;启用扩展。这里有个小坑有些云数据库默认没放开扩展权限需要在控制台或提工单解决本地开发一般没这个问题。接下来要建一张表来存放向量。虽然 Spring AI 的自动配置能帮你建但我建议还是手动建目的之一是让你清楚结构里到底存了什么。核心字段无外乎三个部分id、content、embedding外加一个 metadata 的 JSONB 字段。Spring AI 的 PostgreSQLVectorStore 实际上有自己的表结构表名默认是vector_store会自动创建你不用手动 pre-create。4.2 引入依赖与最小化配置在 Spring Boot 项目里引入相关依赖时要选和 Spring Boot 版本对应的 Spring AI 版本。我用 Spring Boot 3.2.x 配 Spring AI 1.0.0 系列依赖坐标比较稳定。简单说你有两个选择一个是引入spring-ai-starter-vector-store-pgvector它会自动配置数据源和向量仓库另一个是手动引入spring-ai-pgvector-store并自己声明 Bean。官方推荐 starter 方式我也建议用 starter因为少写代码配置项也暴露得清晰。application.yml 里大致是这样spring: datasource: url: jdbc:postgresql://localhost:5432/ai_demo username: postgres password: postgres ai: vectorstore: pgvector: index-type: HNSW distance-type: COSINE_DISTANCE dimensions: 1536这段配置的重点有两个。一是index-type和distance-type选 HNSW 加余弦距离在中型数据集上表现稳定二是dimensions它必须和你的 EmbeddingModel 输出的向量维度严格一致。如果通过 OpenAI 的 text-embedding-3-small 嵌入维度是 1536其他模型就按实际输出填。4.3 把文档写入向量库核心代码就那么几行在 Spring 容器里注入 VectorStore 后写入的核心代码非常短Autowired private VectorStore vectorStore; public void storeDocument(String content, MapString, Object metadata) { ListDocument docs List.of(new Document(content, metadata)); vectorStore.add(docs); }不要把这段代码理解成“只存了文本和元数据”。add方法内部会拿注入的 EmbeddingModel 把 content 转成向量再连同 id、元数据一起写入底层。所以调用方拿到的只是起点真正的开销在嵌入模型调用和数据库写入上。如果一次性要写几千个文档注意不要写一条存一条。合理的做法是批量组装 Document 列表一次 add 一批。一个常见的性能误区是循环里同步调用向量化接口导致几十上百个文档入库要等几分钟实际上用批量提交、必要时用虚拟线程或并行流只跑嵌入部分整体耗时能降到能接受的水平。4.4 检索让用户的问题去匹配知识库检索的 API 同样很简洁public ListDocument search(String query, int topK) { SearchRequest request SearchRequest.builder() .query(query) .topK(topK) .build(); return vectorStore.similaritySearch(request); }这段代码做的事是先把 query 向量化再走库里的 ANN 索引找最接近的 topK 个文档最后把 Document 列表带回来。检索结果里包含着原文和当时写的 metadata正好可以直接拼进 prompt 或者做后处理。TopK 的取值建议在 36 之间太小容易漏太大会把无关内容塞进上下文使模型回答质量下降。多个验证数据表明单轮问答里 45 段相关上下文往往是最优区间多了反而造成干扰。4.5 嵌入模型调用链其实是可以替换的很多人在配向量存储时忘了一个前提得先有 EmbeddingModel。如果用 OpenAI 就走OpenAiEmbeddingModel如果本地就配OllamaEmbeddingModel或ONNXEmbeddingModel。它们实现同一个接口VectorStore 根本不需要区分背后是谁。这里有个经常踩的坑开发环境配了本地嵌入模型生产环境切到 OpenAI 嵌入模型结果线上库里的旧向量全部失效。原因就是维度或向量分布变了。要规避的话要么全生命周期只用一个嵌入模型要么旧数据统一重新向量化没有第三条路。5. 常见问题与排查技巧实录5.1 检索结果乱七八糟问题多半不在数据库向量数据库返回的结果不准很多人第一反应是换产品实际上八成问题是出在前面两环切分和嵌入。切分太粗一段文本里混了好几个主题向量是各主题的混合平均查什么都不太匹配。切分太细一个完整知识点被拆断检索到的只是半截话。嵌入模型选得太弱语义理解能力跟不上向量本身就“没看懂”文本。排查顺序我一般这样走先随机抽查几条存储的 Document看内容切得是否有完整上下文再对着同一句话分别用查询文本和文档文本看嵌入后的向量相似度如果相似度很低再考虑换嵌入模型。5.2 数据量一上去查询就变慢索引和硬件总有一个短板很多从原型转入生产的项目都会遇到类似情况几万条数据时检索毫秒级到几十万条突然要几百毫秒甚至几秒。检查重点有两方面第一是否建了合适的向量索引。没建索引时数据库只能全表扫向量维度又高慢是正常的。HNSW 索引在构建时会占用额外内存构建过程也比较耗时但建完之后查询体验提升明显。第二查询是否带了高选择性过滤条件。即便有索引如果过滤条件筛选出来的是全表中极小一部分数据库可能放弃索引走全表扫描。这时候需要靠执行计划去确认不能靠猜。5.3 Spring AI 自动配置的常见冲突问题同时引入 pgvector starter 和实体内嵌数据库依赖时可能出现数据源冲突。Spring AI 的自动配置优先使用已定义的数据源如果只有一个数据源一般没事可一旦有多个它就不知道该往哪个数据源建表。解决方案是显式声明 PostgreSQLVectorStore 的 Bean指定具体的 JdbcTemplate 数据源。另一个常见问题是在没有 VectorStore 任何实现的情况下直接注入 VectorStore 字段启动直接报错找不到 Bean。解决方案很简单让自动配置生效或手动声明一个实现就行。这类问题通常错误日志比较明确主要别慌按缺少哪种类型的 Bean 去定位。5.4 常见问题速查表现象根因处理方式检索结果语义不符切分粒度不合理或嵌入模型不合适调整切分块大小换嵌入模型重试入库速度极慢单条同步调用嵌入接口批量向量化控制并发查询越来越慢没有向量索引或索引退化建 HNSW 索引重建索引启动报找不到 VectorStore Bean缺少向量存储实现引入对应 starter 或手动声明实现切换嵌入模型后检索效果变差向量空间不一致统一嵌入模型或重新向量化全量数据检索准确但模型没用上TopK 上下游丢失或 prompt 拼接问题检查上下文传给 chat 模型的完整链路元数据过滤失效过滤字段和库中 metadata 结构不一致核对写入和查询时的字段名与类型6. 几个容易被忽略的运维级别细节6.1 向量库的备份与恢复不能只用业务数据备份糊弄pgvector 本质上是 PostgreSQL 的扩展备份方式与普通数据表基本一致。但容易忽略的是向量数据量大单次全量备份的耗时和空间成本比普通业务表高出不少。生产环境建议把向量表单独规划备份策略比如降低全量备份频率辅以按时间增量备份。另外向量索引在恢复之后通常需要重建尤其 HNSW 这类内存敏感索引。恢复数据后直接跑查询可能碰到“索引文件损坏”或“无法加载索引”的报错。应对方法很简单恢复后主动REINDEX。这类经验不实际操作一次很难提前意识得到。6.2 监控项不要只盯 CPU向量化调用量更关键运行一个 RAG 服务嵌入调用和 chat 调用一样会消耗外部 API 配额。嵌入调用虽然单次便宜但入库一批文档动辄成千上万次费用累计很快。线上要同时监控外部接口的错误率、平均延迟和每日调用量。尤其是并发入库时很容易因为触发限流导致大批写入失败而且这种失败往往是静默的——某些客户端会重试某些不会最终出现“库里少了一批文档”的诡异问题。我的建议是给入库流程加一个简单的执行记录批次号、成功条数、失败条数、失败原因。排查数据缺失问题时这个记录能省下大把时间。6.3 关于维度、批次大小和内存的实测经验以 1536 维向量为例一条向量本身占用的空间大约是 1536 乘 4 字节也就是 6KB 左右。存 100 万条原始数据就接近 6GB再加上 HNSW 索引的额外内存开销一个单节点服务的内存消耗往往会到十几甚至二十几 GB。很多团队把向量库和业务数据库部署在一个小规格实例上用着用着就 OOM。算清账再定规格是运维向量库的第一步。批次大小上写入 5001000 条一批比较均衡批次太小时网络和事务开销占比高批次太大又可能撑爆内存。具体值还要结合嵌入接口的限流情况调节没有万能数字。7. 这篇笔记的最后一点私货.Part 说实话向量数据库在整个 Spring AI 技术栈里属于“看着简单、用深了全是坑”的部分。接口确实封装得好两行代码就能写入和检索但真正决定一个 RAG 应用效果好坏的恰恰是接口背后那些不起眼的细节嵌入模型选得对不对、文档切得细不细、索引建没建、元数据过滤设计得好不好、备份策略有没有跟上。这些点官方文档不会替你决定得靠自己在实际项目里一条条验证。我个人多次调整后的建议是新项目起步阶段不要贪多求全直接一个 PostgreSQL 加 pgvector把完整链路先跑通再拿真实数据去压测。等数据量和并发真的大到 pgvector 吃力时再考虑迁移或引入专业向量数据库也不迟。技术选型从来不是选最强大的而是选最适合当前阶段、过渡成本最低的。如果你已经在 Spring AI 里跑通了向量存储下一步建议去试试多租户场景下的元数据隔离、混合检索关键词加向量以及把历史对话也向量化后参与检索。这些都是 RAG 应用走向实用前必然要解决的问题等我把这几个方向的实测结果整理完再接着往下写。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑