Spring AI 学习篇(六)| 向量数据库的选择与Spring AI集成
- 一、本章核心学习目标
- 二、前置知识准备
- 三、为什么我们需要向量数据库?
- 四、2026年主流向量数据库对比与选型
-
- [1. 向量数据库核心选型指标](#1. 向量数据库核心选型指标)
- [2. 主流向量数据库横向对比](#2. 主流向量数据库横向对比)
- [3. 向量数据库选型决策树](#3. 向量数据库选型决策树)
- [五、Spring AI向量数据库统一API详解](#五、Spring AI向量数据库统一API详解)
-
- [1. 本地开发首选:Chroma集成](#1. 本地开发首选:Chroma集成)
-
- [(1) 添加Maven依赖](#(1) 添加Maven依赖)
- [(2) 基础配置(application.yml)](#(2) 基础配置(application.yml))
- [(3) 启动本地Chroma服务](#(3) 启动本地Chroma服务)
- [2. VectorStore核心API](#2. VectorStore核心API)
- [3. 完整代码示例](#3. 完整代码示例)
-
- [(1) 注入VectorStore](#(1) 注入VectorStore)
- [(2) 添加文档到向量数据库](#(2) 添加文档到向量数据库)
- [(3) 相似度检索](#(3) 相似度检索)
- [(4) 删除文档](#(4) 删除文档)
- [4. 切换到其他向量数据库](#4. 切换到其他向量数据库)
-
- [(1) 修改依赖](#(1) 修改依赖)
- [(2) 修改配置](#(2) 修改配置)
- 六、企业级最佳实践
-
- [1. 合理设置Top K值](#1. 合理设置Top K值)
- [2. 善用元数据过滤](#2. 善用元数据过滤)
- [3. 批量导入数据](#3. 批量导入数据)
- [4. 定期备份数据](#4. 定期备份数据)
- [5. 选择合适的索引类型](#5. 选择合适的索引类型)
- 七、常见坑与解决方案
-
- [1. ❌ 嵌入模型与向量数据库维度不匹配](#1. ❌ 嵌入模型与向量数据库维度不匹配)
- [2. ❌ 检索结果不准确](#2. ❌ 检索结果不准确)
- [3. ❌ 批量导入内存溢出](#3. ❌ 批量导入内存溢出)
- [4. ❌ Chroma嵌入式模式性能差](#4. ❌ Chroma嵌入式模式性能差)
- [5. ❌ 忘记添加元数据](#5. ❌ 忘记添加元数据)
- 八、本章总结与下章预告
- 九、课后练习
一、本章核心学习目标
学完本章,你将能够:
- 深刻理解向量数据库的本质和解决的核心问题
- 独立完成主流向量数据库的对比与选型
- 熟练使用Spring AI向量数据库统一API
- 快速搭建本地Chroma向量数据库并完成CRUD操作
- 实现向量相似度检索与批量数据导入
- 掌握向量数据库的企业级最佳实践
- 避开向量数据库使用中的常见坑
二、前置知识准备
- 已经完成第5篇的学习,理解嵌入模型与向量表示的本质
- 熟练使用Spring AI
EmbeddingClient生成向量 - 了解关系型数据库的基本原理和使用方法
- 熟悉Spring Boot的依赖注入和配置管理
三、为什么我们需要向量数据库?
在第5篇我们已经学会了如何将文本转换为向量,但新的问题来了:如何高效地存储和检索这些向量?
很多人会问:"我能不能用MySQL存向量?"答案是:可以,但性能极差。
传统关系型数据库的局限性
假设你有100万条向量数据,每条向量是1024维。当用户发起一个查询时,你需要:
- 将用户的问题转换为向量
- 遍历数据库中所有100万条向量
- 计算每条向量与查询向量的余弦相似度
- 排序后返回最相似的Top 10条结果
这个过程需要进行100万次向量计算,在MySQL中可能需要几十秒甚至几分钟才能完成,完全无法满足实时性要求。
向量数据库的核心优势
向量数据库是专门为存储和检索向量设计的数据库,它通过**近似最近邻搜索(ANN)**算法,将检索时间从O(n)降低到O(log n),可以在毫秒级从百万级甚至亿级数据中找到最相似的结果。
核心能力对比:
| 数据库类型 | 存储能力 | 检索速度 | 相似度计算 | 适用场景 |
|---|---|---|---|---|
| MySQL | 支持 | 秒级/分钟级 | 精确计算 | 小数据量(<1万条) |
| 向量数据库 | 支持 | 毫秒级 | 近似计算 | 大数据量(>1万条) |
预告式提及:向量数据库是RAG系统的核心存储组件,下一章我们将把嵌入模型和向量数据库结合起来,实现一个完整的基础RAG系统。
四、2026年主流向量数据库对比与选型
1. 向量数据库核心选型指标
| 指标 | 含义 | 影响 |
|---|---|---|
| 部署难度 | 安装和配置的复杂程度 | 影响开发和运维成本 |
| 性能 | 单节点每秒查询次数(QPS) | 影响系统的并发能力 |
| 扩展性 | 是否支持分布式集群 | 影响系统的容量上限 |
| Spring AI支持 | 是否有官方原生支持 | 影响开发效率 |
| 开源协议 | 是否可以免费商用 | 关系到企业的法律风险 |
2. 主流向量数据库横向对比
| 数据库名称 | 类型 | 部署难度 | 单节点QPS | 分布式支持 | Spring AI支持 | 开源协议 | 适用场景 |
|---|---|---|---|---|---|---|---|
| Chroma | 轻量本地 | 极低(无需单独部署) | 1000+ | ❌ 不支持 | ✅ 官方原生 | Apache 2.0 | 本地开发、原型验证、小团队内部使用(<100万条) |
| Qdrant | 生产级 | 中等 | 5000+ | ✅ 支持 | ✅ 官方原生 | Apache 2.0 | 中小规模生产环境(100万-1亿条) |
| Milvus | 生产级 | 较高 | 10000+ | ✅ 支持 | ✅ 官方原生 | Apache 2.0 | 大规模生产环境(>1亿条) |
| Redis Stack | 缓存型 | 极低 | 10000+ | ✅ 支持 | ✅ 官方原生 | RSAL(源码可用) | 已有Redis基础设施、高并发场景 |
| Pinecone | 云服务 | 极低 | 10000+ | ✅ 支持 | ✅ 官方原生 | 商业 | 不想自己运维的云原生场景 |
3. 向量数据库选型决策树
向量数据库选型决策树:
├── 本地开发/原型验证 → Chroma(首选,无需部署,开箱即用)
├── 生产环境
│ ├── 已有Redis基础设施 → Redis Stack
│ ├── 数据量<1亿条 → Qdrant(部署简单,性能优秀)
│ ├── 数据量>1亿条 → Milvus(扩展性最好)
│ └── 不想自己运维 → Pinecone(云服务)
2026年最新进展:Chroma已经发布了1.0稳定版,性能和稳定性大幅提升,完全可以满足小团队和中小规模生产环境的需求。Spring AI对所有主流向量数据库都提供了官方原生支持,切换数据库只需要修改依赖和配置,业务代码完全不需要改变。
五、Spring AI向量数据库统一API详解
和ChatClient、EmbeddingClient一样,Spring AI为所有向量数据库提供了统一的VectorStore接口。无论你使用的是Chroma、Qdrant还是Milvus,代码完全相同。
1. 本地开发首选:Chroma集成
Chroma是本地开发和原型验证的最佳选择,它不需要单独部署,会自动在本地创建文件存储。
(1) 添加Maven依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency>
(2) 基础配置(application.yml)
yaml
spring:
ai:
# 嵌入模型配置(我们使用本地Ollama BGE-M4)
ollama:
base-url: http://localhost:11434
embedding:
model: bge-m4
# Chroma向量数据库配置
vectorstore:
chroma:
host: http://localhost:8000
collection-name: spring-ai-demo
(3) 启动本地Chroma服务
虽然Chroma可以嵌入式运行,但推荐使用Docker启动独立服务,性能更好且数据更安全:
cmd
docker run -d -p 8000:8000 -v D:\chroma-data:/chroma/chroma chromadb/chroma:1.0.0
2. VectorStore核心API
Spring AI的VectorStore接口提供了以下核心操作(方法签名以 Spring AI 1.0 实际 API 为准):
java
// 添加文档
void add(List<Document> documents);
// 删除文档
void delete(String idList); // 或 delete(List<String> idList)
// 相似度检索
List<Document> similaritySearch(SearchRequest request);
// 检索结果自带分数,通过 doc.getScore() 获取
注意:不同版本 Spring AI 的方法签名可能有细微差异,以 IDE 自动补全和官方文档为准。
3. 完整代码示例
(1) 注入VectorStore
java
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/vector")
public class VectorStoreController {
private final VectorStore vectorStore;
// 构造函数注入VectorStore,Spring AI会自动为我们创建实例
public VectorStoreController(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
(2) 添加文档到向量数据库
java
// 添加单个文档
@PostMapping("/add")
public String addDocument(@RequestParam String content) {
Document document = new Document(content);
vectorStore.add(List.of(document));
return "文档添加成功,ID:" + document.getId();
}
// 批量添加文档
@PostMapping("/add/batch")
public List<String> addDocuments(@RequestBody List<String> contents) {
List<Document> documents = contents.stream()
.map(Document::new)
.toList();
vectorStore.add(documents);
return documents.stream()
.map(Document::getId)
.toList();
}
// 添加带元数据的文档
@PostMapping("/add-with-metadata")
public String addDocumentWithMetadata(@RequestParam String content,
@RequestParam String author,
@RequestParam String category) {
Map<String, Object> metadata = Map.of(
"author", author,
"category", category,
"createTime", System.currentTimeMillis()
);
Document document = new Document(content, metadata);
vectorStore.add(List.of(document));
return "文档添加成功,ID:" + document.getId();
}
(3) 相似度检索
java
// 基础相似度检索,返回Top 5个最相似的文档
@GetMapping("/search")
public List<Document> search(@RequestParam String query) {
return vectorStore.similaritySearch(
SearchRequest.builder()
.query(query)
.topK(5)
.build()
);
}
// 带分数的相似度检索
@GetMapping("/search-with-scores")
public List<Map<String, Object>> searchWithScores(@RequestParam String query) {
return vectorStore.similaritySearch(
SearchRequest.builder()
.query(query)
.topK(5)
.build()
).stream()
.map(doc -> Map.of(
"content", doc.getContent(),
"score", doc.getScore(),
"metadata", doc.getMetadata()
))
.toList();
}
// 带过滤条件的相似度检索
@GetMapping("/search-with-filter")
public List<Document> searchWithFilter(@RequestParam String query,
@RequestParam String category) {
return vectorStore.similaritySearch(
SearchRequest.builder()
.query(query)
.topK(5)
.filterExpression("category == '" + category + "'")
.build()
);
}
(4) 删除文档
java
// 根据ID删除文档
@DeleteMapping("/delete")
public String deleteDocument(@RequestParam String id) {
vectorStore.delete(List.of(id));
return "文档删除成功";
}
4. 切换到其他向量数据库
这是Spring AI最强大的功能之一。切换到Qdrant只需要两步:
(1) 修改依赖
xml
<!-- 注释掉Chroma依赖 -->
<!-- <dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency> -->
<!-- 添加Qdrant依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-qdrant</artifactId>
</dependency>
(2) 修改配置
yaml
spring:
ai:
# 注释掉Chroma配置
# vectorstore:
# chroma:
# host: http://localhost:8000
# collection-name: spring-ai-demo
# 添加Qdrant配置
vectorstore:
qdrant:
host: http://localhost:6333
collection-name: spring-ai-demo
不需要修改任何一行业务代码,所有之前写的接口都可以正常使用。
六、企业级最佳实践
1. 合理设置Top K值
- 一般设置为3-10个结果
- 太少会遗漏相关信息,太多会引入无关信息
- 可以根据实际场景动态调整
2. 善用元数据过滤
在文档中添加元数据(如作者、分类、创建时间等),检索时可以通过元数据过滤结果,大幅提升检索准确率。
3. 批量导入数据
不要每次只添加一个文档,尽量批量导入,这样可以大幅提升效率。
java
// 推荐:批量导入
List<Document> documents = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
documents.add(new Document("文档" + i));
}
vectorStore.add(documents);
// 不推荐:逐个添加
for (int i = 0; i < 1000; i++) {
vectorStore.add(List.of(new Document("文档" + i)));
}
4. 定期备份数据
向量数据库中的数据是宝贵的资产,一定要定期备份。Chroma可以直接备份数据目录,Milvus和Qdrant提供了官方的备份工具。
5. 选择合适的索引类型
不同的向量数据库支持不同的索引类型,选择合适的索引类型可以平衡检索速度和准确率:
- HNSW:速度快,准确率高,内存占用大(推荐)
- IVF:速度快,准确率稍低,内存占用小
- FLAT:准确率最高,速度最慢,适合小数据量
七、常见坑与解决方案
1. ❌ 嵌入模型与向量数据库维度不匹配
问题 :嵌入模型生成的向量维度和向量数据库集合的维度不一致,导致添加文档失败
解决方案:创建集合时明确指定维度,确保与嵌入模型的维度一致。BGE-M4是1024维,DeepSeek是1536维。
2. ❌ 检索结果不准确
问题 :相似度检索返回的结果与查询不相关
解决方案:
- 检查嵌入模型是否选择正确(中文场景优先使用BGE-M4)
- 调整Top K值
- 添加元数据过滤
- 优化文本切分策略(我们会在第7篇详细讲解)
3. ❌ 批量导入内存溢出
问题 :一次性导入大量文档导致内存溢出
解决方案:分批次导入,每次导入100-1000个文档。
4. ❌ Chroma嵌入式模式性能差
问题 :使用Chroma嵌入式模式,数据量大时性能很差
解决方案:使用Docker启动独立的Chroma服务,性能会显著提升。
5. ❌ 忘记添加元数据
问题 :添加文档时没有添加元数据,后续无法进行过滤和管理
解决方案:养成添加元数据的习惯,至少包含创建时间、来源和分类。
八、本章总结与下章预告
本章总结
- 向量数据库是专门为存储和检索向量设计的数据库,通过ANN算法实现毫秒级检索
- 本地开发首选Chroma,生产环境根据数据量选择Qdrant或Milvus
- Spring AI提供了统一的
VectorStore接口,切换数据库只需要修改依赖和配置 VectorStore支持添加、删除、相似度检索和元数据过滤等核心操作- 合理设置Top K值、善用元数据过滤和批量导入可以大幅提升系统性能
预告式提及:我们现在已经掌握了嵌入模型和向量数据库这两个RAG的核心组件。下一章我们将把它们结合起来,从零实现一个完整的基础RAG系统,解决大模型的知识截止和幻觉问题。
下章预告
下一章我们将学习从零实现一个基础RAG系统。你将学会:
- RAG的完整工作流程与原理详解
- 文档解析:支持PDF、Word、Markdown等多种格式
- 文本切分策略:固定长度切分 vs 语义切分
- 向量索引构建与检索流程
- 基础RAG问答接口的完整实现
九、课后练习
- 使用Docker启动本地Chroma服务,配置Spring AI集成
- 添加10篇关于Java和Spring AI的文档到向量数据库
- 实现一个相似度检索接口,测试不同查询的返回结果
- 为文档添加元数据(分类:Java、Spring AI),实现带分类过滤的检索
- 尝试将向量数据库切换为Qdrant,观察业务代码是否需要修改