第10天:Vector Store 集成——Milvus / Redis / PostgreSQL 全面解析
系列文章:Spring AI Alibaba 技术博客系列
难度等级:⭐⭐⭐⭐
前置知识:Embedding 向量嵌入基础、Spring Boot 基础
本篇目标:深入理解 Vector Store 在 AI 应用中的角色,掌握 Milvus、Redis、PostgreSQL 三种主流向量数据库的集成方式与选型策略
目录
- 为什么需要 Vector Store?
- Vector Store 在 Spring AI Alibaba 中的抽象设计
- Milvus 集成实战
- Redis 向量检索集成实战
- PostgreSQL (pgvector) 集成实战
- 三种 Vector Store 对比与选型指南
- 最佳实践与性能优化
- 常见问题与排查
1. 为什么需要 Vector Store?
在上一篇文章中,我们学习了 Embedding 向量嵌入——将文本转换为高维向量表示。但一个核心问题随之而来:这些向量如何高效存储和检索?
传统的关系型数据库擅长精确匹配和范围查询,但对于”语义相似度搜索”这种模糊匹配场景,效率极低。想象一下,如果你有100万条文档的向量(每条768维),要找到与查询向量最相似的Top-K条记录,全表扫描的复杂度是 O(N×D),N为向量数,D为维度数——这在实时场景下是不可接受的。
Vector Store(向量数据库)就是为这个问题而生的。它通过以下核心技术实现高效检索:
- ANN(Approximate Nearest Neighbor)算法:如 HNSW、IVF、PQ 等,将搜索复杂度降至 O(log N) 级别
- 向量索引结构:在向量空间上构建索引,支持快速相似度计算
- 元数据过滤:在向量检索的同时支持条件过滤(如按时间、标签、用户ID等)
- 分布式扩展:支持海量向量数据的水平扩展
Vector Store 在 RAG 架构中的位置
┌─────────────────────────────────────────────────────────┐
│ RAG 整体架构 │
│ │
│ 用户提问 ──→ Prompt 工程 ──→ LLM ──→ 回答 │
│ ↑ │
│ 检索结果 │
│ ↑ │
│ 查询向量 ←── Embedding Model │
│ ↑ │
│ ┌────────┴────────┐ │
│ │ Vector Store │ ←── 向量 + 元数据 │
│ │ (Milvus/Redis/ │ │
│ │ PostgreSQL) │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
2. Vector Store 在 Spring AI Alibaba 中的抽象设计
Spring AI Alibaba 基于 Spring AI 的标准接口,对 Vector Store 进行了统一的抽象设计。核心接口如下:
2.1 VectorStore 接口
package org.springframework.ai.vectorstore;
public interface VectorStore {
/**
* 添加文档(自动将文档内容通过 EmbeddingModel 转为向量后存储)
*/
void add(List<Document> documents);
/**
* 删除文档
*/
boolean delete(List<String> idList);
/**
* 相似度搜索
*/
List<Document> similaritySearch(SearchRequest request);
/**
* 搜索请求构建器
*/
interface SearchRequest {
static SearchRequest query(String query) { ... }
SearchRequest withTopK(int topK);
SearchRequest withSimilarityThreshold(double threshold);
SearchRequest withFilterExpression(String filterExpression);
}
}
2.2 设计哲学
Spring AI Alibaba 的 Vector Store 设计遵循以下原则:
- 透明化 Embedding:开发者只需传入 Document 对象,底层自动调用 EmbeddingModel 进行向量化,无需手动处理向量转换
- 统一的搜索接口:无论底层使用哪种向量数据库,搜索 API 保持一致
- 可插拔实现:通过 Spring Boot 的自动配置机制,切换 Vector Store 只需修改依赖和配置
2.3 Document 数据结构
public class Document {
private String id; // 文档唯一标识
private String text; // 文档文本内容
private Map<String, Object> metadata; // 元数据(用于过滤)
private MediaType mediaType; // 媒体类型
// ...
}
metadata 字段是 Vector Store 过滤能力的核心。例如:
Document doc = new Document(
"doc-001",
"Spring Boot 3.0 引入了虚拟线程支持...",
Map.of(
"source", "spring-boot-docs",
"category", "framework",
"version", "3.0",
"created_at", "2026-05-04"
)
);
3. Milvus 集成实战
Milvus 是目前最流行的开源向量数据库之一,由 Zilliz 公司开发,支持十亿级向量检索,具有出色的性能和丰富的索引类型。
3.1 Milvus 简介
Milvus 的核心特性:
- 多种索引类型:IVF_FLAT、IVF_PQ、HNSW、DISKANN、SCANN 等
- 多种距离度量:L2、IP(内积)、COSINE(余弦相似度)
- 混合搜索:向量检索 + 标量过滤
- 分布式架构:支持水平扩展,适合生产环境
- 多语言 SDK:Java、Python、Go、Node.js 等
3.2 环境准备
使用 Docker 快速部署 Milvus 单机版:
# 下载 docker-compose 配置文件
wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
# 启动 Milvus
docker-compose up -d
启动后,Milvus 会在 19530 端口提供服务,管理界面在 9121 端口。
3.3 添加 Maven 依赖
<dependencies>
<!-- Spring AI Alibaba -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M6.1</version>
</dependency>
<!-- Milvus Vector Store -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-milvus-store-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
3.4 配置文件
spring:
ai:
# DashScope Embedding 配置
dashscope:
api-key: ${DASHSCOPE_API_KEY}
embedding:
options:
model: text-embedding-v3
dimensions: 1024
# Milvus Vector Store 配置
vectorstore:
milvus:
host: localhost
port: 19530
databaseName: default
collectionName: spring_ai_documents
indexType: HNSW # 索引类型
metricType: COSINE # 距离度量方式
embeddingDimension: 1024 # 向量维度(需与 Embedding 模型一致)
username: "" # 可选
password: "" # 可选
3.5 核心代码示例
3.5.1 向量存储配置类
@Configuration
public class VectorStoreConfig {
@Bean
public CommandLineRunner initVectorStore(VectorStore vectorStore) {
return args -> {
log.info("Vector Store 初始化完成,类型: {}", vectorStore.getClass().getSimpleName());
};
}
}
3.5.2 文档入库服务
@Service
@Slf4j
@RequiredArgsConstructor
public class DocumentIngestionService {
private final VectorStore vectorStore;
/**
* 批量导入文档到向量库
*/
public void ingestDocuments(List<String> texts, String source, String category) {
List<Document> documents = texts.stream()
.map(text -> new Document(
UUID.randomUUID().toString(),
text,
Map.of(
"source", source,
"category", category,
"ingested_at", LocalDateTime.now().toString()
)
))
.toList();
vectorStore.add(documents);
log.info("成功导入 {} 条文档到 Vector Store", documents.size());
}
/**
* 从文件导入(简化版)
*/
public void ingestFromFile(Path filePath, String source) throws IOException {
String content = Files.readString(filePath);
// 实际项目中应该使用 TextSplitter 进行分块
// 这里简化处理,按段落分割
List<String> chunks = Arrays.asList(content.split("\n\n"));
ingestDocuments(chunks, source, "file");
}
}
3.5.3 向量检索服务
@Service
@Slf4j
@RequiredArgsConstructor
public class VectorSearchService {
private final VectorStore vectorStore;
/**
* 基础相似度搜索
*/
public List<Document> search(String query, int topK) {
return vectorStore.similaritySearch(
SearchRequest.query(query)
.withTopK(topK)
.withSimilarityThreshold(0.7)
);
}
/**
* 带过滤条件的搜索
*/
public List<Document> searchWithFilter(String query, int topK,
String category, String source) {
// 构建过滤表达式
StringBuilder filter = new StringBuilder();
if (category != null) {
filter.append("category == '").append(category).append("'");
}
if (source != null) {
if (!filter.isEmpty()) filter.append(" && ");
filter.append("source == '").append(source).append("'");
}
return vectorStore.similaritySearch(
SearchRequest.query(query)
.withTopK(topK)
.withSimilarityThreshold(0.7)
.withFilterExpression(filter.toString())
);
}
/**
* 删除文档
*/
public boolean deleteDocuments(List<String> documentIds) {
boolean result = vectorStore.delete(documentIds);
log.info("删除文档结果: {}, IDs: {}", result, documentIds);
return result;
}
}
3.5.4 REST API 控制器
@RestController
@RequestMapping("/api/vector")
@RequiredArgsConstructor
@Slf4j
public class VectorStoreController {
private final DocumentIngestionService ingestionService;
private final VectorSearchService searchService;
/**
* 搜索接口
*/
@PostMapping("/search")
public ResponseEntity<List<Map<String, Object>>> search(
@RequestBody SearchRequestDTO request) {
List<Document> results;
if (request.hasFilter()) {
results = searchService.searchWithFilter(
request.getQuery(),
request.getTopK(),
request.getCategory(),
request.getSource()
);
} else {
results = searchService.search(request.getQuery(), request.getTopK());
}
List<Map<String, Object>> response = results.stream()
.map(doc -> Map.of(
"id", doc.getId(),
"text", doc.getText(),
"metadata", doc.getMetadata(),
"score", doc.getMetadata().get("score")
))
.toList();
return ResponseEntity.ok(response);
}
/**
* 批量导入接口
*/
@PostMapping("/ingest")
public ResponseEntity<String> ingest(@RequestBody IngestRequest request) {
ingestionService.ingestDocuments(
request.getTexts(),
request.getSource(),
request.getCategory()
);
return ResponseEntity.ok("导入成功");
}
// DTO 类
public record SearchRequestDTO(
String query,
int topK,
String category,
String source
) {
public boolean hasFilter() {
return category != null || source != null;
}
}
public record IngestRequest(
List<String> texts,
String source,
String category
) {}
}
3.6 Milvus 索引类型选择
Milvus 提供多种索引类型,选择合适的索引对性能至关重要:
| 索引类型 | 适用场景 | 内存占用 | 搜索速度 | 构建速度 |
|---|---|---|---|---|
| FLAT | 小数据量(<1万),精确搜索 | 低 | 慢 | 无 |
| IVF_FLAT | 中等数据量,均衡型 | 中 | 中 | 中 |
| IVF_PQ | 大数据量,内存受限 | 低 | 中 | 中 |
| HNSW | 高召回率要求,内存充足 | 高 | 快 | 慢 |
| DISKANN | 超大数据量(亿级),磁盘存储 | 极低 | 快 | 慢 |
选择建议:
- 数据量 < 10万:HNSW(M=16, efConstruction=64)
- 数据量 10万~100万:IVF_FLAT(nlist=1024)
- 数据量 > 100万:IVF_PQ 或 DISKANN
4. Redis 向量检索集成实战
Redis 7.2+ 版本通过 RediSearch 模块原生支持向量检索。如果你的项目已经在使用 Redis,集成向量检索是非常自然的选择。
4.1 Redis 向量检索简介
Redis 向量检索的特点:
- 一站式存储:向量、文档、元数据、缓存共存于 Redis
- 低延迟:内存数据库,响应时间通常在毫秒级
- HNSW + FLAT 索引:支持两种索引算法
- 混合查询:向量搜索 + 全文搜索 + 精确过滤
- Redis Stack:通过 Redis Stack 一键部署所有模块
4.2 环境准备
# 使用 Redis Stack(包含 RediSearch、RedisJSON 等模块)
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 \
redis/redis-stack:latest
4.3 添加 Maven 依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-store-spring-boot-starter</artifactId>
</dependency>
4.4 配置文件
spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
embedding:
options:
model: text-embedding-v3
dimensions: 1024
vectorstore:
redis:
host: localhost
port: 6379
index: spring_ai_index
prefix: "doc:"
initialize-schema: true # 自动创建索引
4.5 Redis Vector Store 核心代码
Redis Vector Store 的使用方式与 Milvus 基本一致,体现了 Spring AI 统一抽象的优势:
@Service
@Slf4j
@RequiredArgsConstructor
public class RedisVectorService {
private final VectorStore vectorStore;
/**
* 入库
*/
public void addDocuments(List<Document> documents) {
vectorStore.add(documents);
log.info("添加 {} 条文档到 Redis Vector Store", documents.size());
}
/**
* 搜索 - 注意 Redis 的过滤表达式语法
*/
public List<Document> search(String query, int topK, String category) {
SearchRequest request = SearchRequest.query(query)
.withTopK(topK)
.withSimilarityThreshold(0.6);
if (category != null) {
// Redis 的过滤语法使用 @field:value 格式
request = request.withFilterExpression("@category:{" + category + "}");
}
return vectorStore.similaritySearch(request);
}
}
4.6 Redis 向量索引的底层原理
Redis 使用 FT.CREATE 命令创建向量索引:
FT.CREATE spring_ai_index
ON HASH
PREFIX 1 "doc:"
SCHEMA
$.vector AS vector VECTOR HNSW 6
TYPE FLOAT32
DIM 1024
DISTANCE_METRIC COSINE
$.text AS text TEXT
$.category AS category TAG
$.source AS source TAG
关键参数解释:
HNSW:使用 HNSW 索引算法TYPE FLOAT32:向量数据类型DIM 1024:向量维度DISTANCE_METRIC COSINE:余弦相似度度量6:后续参数数量(TYPE, DIM, DISTANCE_METRIC 各占2个参数)
4.7 Redis 作为 Vector Store 的适用场景
适合:
- 已有 Redis 基础设施,想快速增加向量检索能力
- 数据量不大(百万级以下)
- 对延迟敏感(毫秒级响应)
- 需要同时做缓存、会话存储、向量检索
不适合:
- 数据量超大规模(十亿级)
- 需要复杂的多租户隔离
- 需要多种索引类型的灵活切换
5. PostgreSQL (pgvector) 集成实战
pgvector 是 PostgreSQL 的向量扩展,让你在关系型数据库中直接进行向量检索。这是目前最轻量、最容易部署的向量存储方案。
5.1 pgvector 简介
pgvector 的核心特点:
- 零额外组件:只需在现有 PostgreSQL 上安装扩展
- SQL 原生支持:可以用 SQL 进行向量查询、JOIN、聚合
- 事务支持:向量操作可以在事务中进行
- IVFFlat + HNSW 索引:PostgreSQL 16+ 支持 HNSW
- 混合查询:向量搜索 + 关系型查询完美结合
5.2 环境准备
# 使用官方 pgvector 镜像
docker run -d --name pgvector \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
pgvector/pgvector:pg16
# 连接并启用扩展
docker exec -it pgvector psql -U postgres -c "CREATE EXTENSION vector;"
5.3 添加 Maven 依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<!-- PostgreSQL JDBC 驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
5.4 配置文件
spring:
datasource:
url: jdbc:postgresql://localhost:5432/vector_db
username: postgres
password: postgres
driver-class-name: org.postgresql.Driver
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY}
embedding:
options:
model: text-embedding-v3
dimensions: 1024
vectorstore:
pgvector:
index-type: HNSW # 索引类型(IVFFLAT 或 HNSW)
distance-type: COSINE_DISTANCE # 距离类型
initialize-schema: true # 自动建表
dimensions: 1024 # 向量维度
5.5 底层 SQL 解析
pgvector 在数据库中创建如下表结构:
-- 创建向量表
CREATE TABLE vector_store (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content TEXT,
metadata JSON,
embedding vector(1024) -- pgvector 类型
);
-- 创建 HNSW 索引
CREATE INDEX ON vector_store
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
搜索时执行的 SQL:
SELECT id, content, metadata,
embedding <=> '[0.1, 0.2, ...]' AS distance -- 余弦距离
FROM vector_store
WHERE metadata->>'category' = 'framework'
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 5;
<=> 是 pgvector 的余弦距离运算符。其他运算符:
<->:欧氏距离(L2)<#>:负内积(用于最大内积搜索)
5.6 PostgreSQL Vector Store 核心代码
@Service
@Slf4j
@RequiredArgsConstructor
public class PgVectorService {
private final VectorStore vectorStore;
private final JdbcTemplate jdbcTemplate;
/**
* 入库
*/
public void addDocuments(List<Document> documents) {
vectorStore.add(documents);
}
/**
* 利用 JdbcTemplate 执行自定义查询
* 展示 pgvector 的优势:可以直接写 SQL
*/
public List<Map<String, Object>> customSearch(String query,
int topK, String category) {
// 注意:实际项目中应该通过 EmbeddingModel 将 query 转为向量
// 这里简化处理
String sql = """
SELECT id, content, metadata,
embedding <=> ?::vector AS similarity
FROM vector_store
WHERE metadata->>'category' = ?
ORDER BY similarity
LIMIT ?
""";
// Spring AI 的 VectorStore 已经封装了 Embedding 过程
// 实际开发推荐直接使用 VectorStore.similaritySearch()
return vectorStore.similaritySearch(
SearchRequest.query(query)
.withTopK(topK)
.withSimilarityThreshold(0.5)
.withFilterExpression("category = '" + category + "'")
).stream()
.map(doc -> Map.of(
"id", doc.getId(),
"text", doc.getText(),
"metadata", doc.getMetadata()
))
.toList();
}
/**
* 利用关系型能力:JOIN 查询
* 这是 pgvector 独有的优势
*/
public List<Map<String, Object>> searchWithJoin(String query, int topK) {
// 假设有 users 表和 document_owners 关联表
String sql = """
SELECT vs.id, vs.content, vs.metadata, u.username,
vs.embedding <=> ?::vector AS similarity
FROM vector_store vs
JOIN document_owners do ON vs.id = do.document_id
JOIN users u ON do.user_id = u.id
WHERE u.is_active = true
ORDER BY similarity
LIMIT ?
""";
// ...
return List.of();
}
}
5.7 pgvector 索引优化
IVFFlat 索引
-- 创建 IVFFlat 索引
CREATE INDEX ON vector_store
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
-- 搜索时设置 probes(影响精度和速度)
SET ivfflat.probes = 10;
lists:聚类中心数,建议 = 数据量 / 1000probes:搜索时探查的聚类数,越大越精确但越慢
HNSW 索引(推荐)
-- 创建 HNSW 索引
CREATE INDEX ON vector_store
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 搜索时设置 ef_search
SET hnsw.ef_search = 40;
m:每个节点的最大连接数(默认16),越大越精确但内存越大ef_construction:构建索引时的搜索宽度(默认64)ef_search:查询时的搜索宽度,越大越精确但越慢
6. 三种 Vector Store 对比与选型指南
6.1 功能对比
| 特性 | Milvus | Redis (RediSearch) | PostgreSQL (pgvector) |
|---|---|---|---|
| 向量索引类型 | HNSW/IVF/PQ/DISKANN/SCANN | HNSW/FLAT | HNSW/IVFFlat |
| 最大数据量 | 十亿级 | 百万级(受内存限制) | 亿级 |
| 距离度量 | L2/IP/COSINE/JACCARD/HAMMING | L2/IP/COSINE | L2/IP/COSINE |
| 元数据过滤 | ✅ 支持复杂表达式 | ✅ 支持 | ✅ SQL 原生支持 |
| 分布式 | ✅ 原生分布式 | ⚠️ Redis Cluster 有限支持 | ⚠️ 需外部方案 |
| 事务支持 | ❌ | ✅ | ✅ |
| 部署复杂度 | 中(需要 etcd + MinIO) | 低(单进程) | 极低(PG 扩展) |
| 运维成本 | 高 | 低 | 低(已有 PG 团队) |
| 社区活跃度 | 高 | 中 | 高 |
| 许可协议 | Apache 2.0 | RSALv2/SSPLv1 | PostgreSQL License |
6.2 选型决策树
你的项目需要向量存储吗?
│
├── 已有 PostgreSQL 且数据量 < 亿级?
│ └── 是 → pgvector(最省事,零额外运维)
│ └── 否 ↓
│
├── 已有 Redis 且数据量 < 百万级?
│ └── 是 → Redis(复用基础设施)
│ └── 否 ↓
│
├── 数据量 > 百万级或需要分布式?
│ └── 是 → Milvus(专业向量数据库)
│ └── 否 ↓
│
└── 需要极简方案?
└── pgvector(一个扩展搞定)
6.3 实际项目中的常见组合
| 项目类型 | 推荐方案 | 理由 |
|---|---|---|
| 小型 RAG Demo | pgvector | 零额外依赖,快速验证 |
| 企业知识库(<50万文档) | Redis 或 pgvector | 运维成本低,已有基础设施 |
| 大规模 AI 平台(>百万文档) | Milvus | 分布式、高性能、多租户 |
| 已有 Redis 的微服务 | Redis | 复用现有 Redis 集群 |
| 金融/医疗等需要事务的场景 | pgvector | ACID 事务保证 |
7. 最佳实践与性能优化
7.1 Embedding 维度选择
spring:
ai:
dashscope:
embedding:
options:
# 选择合适的维度
# text-embedding-v3 支持:
# - 1024 维(推荐,精度最高)
# - 768 维(兼容性好)
# - 512 维(存储节省)
dimensions: 1024
维度选择原则:
- 维度越高,语义表达能力越强,但存储和计算开销越大
- text-embedding-v3 的 1024 维是性价比最高的选择
- 存储节省公式:
100万条 × 1024维 × 4字节 = 4GB
7.2 分块策略(Chunking)
向量检索的精度很大程度上取决于文本分块策略:
@Service
public class DocumentSplitterService {
/**
* 固定大小分块(最简单)
*/
public List<String> chunkBySize(String text, int chunkSize, int overlap) {
List<String> chunks = new ArrayList<>();
int start = 0;
while (start < text.length()) {
int end = Math.min(start + chunkSize, text.length());
chunks.add(text.substring(start, end));
start = end - overlap; // 重叠区域保持上下文连贯
}
return chunks;
}
/**
* 按段落分块(推荐用于技术文档)
*/
public List<String> chunkByParagraph(String text) {
return Arrays.stream(text.split("\n\n"))
.filter(p -> !p.trim().isEmpty())
.toList();
}
/**
* 递归分块(推荐用于长文档)
* 先按章节分,再按段落分,最后按句子分
*/
public List<String> recursiveChunk(String text, int maxChunkSize) {
// 第一层:按章节
List<String> chunks = splitByPattern(text, "\n#+ ");
// 第二层:对超大块按段落分割
List<String> result = new ArrayList<>();
for (String chunk : chunks) {
if (chunk.length() > maxChunkSize) {
result.addAll(chunkByParagraph(chunk));
} else {
result.add(chunk);
}
}
// 第三层:对仍然超大的块按句子分割
return result.stream()
.flatMap(c -> {
if (c.length() > maxChunkSize) {
return splitByPattern(c, "[。!?]").stream();
}
return Stream.of(c);
})
.filter(s -> !s.trim().isEmpty())
.toList();
}
}
分块大小建议:
- 512~1024 字符:适合问答系统
- 1024~2048 字符:适合技术文档
- 2048~4096 字符:适合长文章摘要
7.3 批量入库优化
@Service
public class BatchIngestionService {
private final VectorStore vectorStore;
// 批量大小建议:100~500
private static final int BATCH_SIZE = 200;
/**
* 批量入库,带进度回调
*/
public void batchIngest(List<Document> documents,
Consumer<IngestProgress> progressCallback) {
int total = documents.size();
int processed = 0;
// 分批处理
for (int i = 0; i < total; i += BATCH_SIZE) {
int end = Math.min(i + BATCH_SIZE, total);
List<Document> batch = documents.subList(i, end);
vectorStore.add(batch);
processed += batch.size();
progressCallback.accept(
new IngestProgress(processed, total,
String.format("%.1f%%", processed * 100.0 / total))
);
}
}
public record IngestProgress(int processed, int total, String percentage) {}
}
7.4 搜索参数调优
public List<Document> optimizedSearch(String query, SearchContext context) {
// 根据场景动态调整参数
SearchRequest request = SearchRequest.query(query);
if (context.isStrict()) {
// 严格模式:高相似度阈值,少结果
request = request
.withTopK(3)
.withSimilarityThreshold(0.85);
} else if (context.isExploratory()) {
// 探索模式:低阈值,多结果
request = request
.withTopK(10)
.withSimilarityThreshold(0.5);
} else {
// 默认模式
request = request
.withTopK(5)
.withSimilarityThreshold(0.7);
}
// 添加元数据过滤
if (context.getCategory() != null) {
request = request.withFilterExpression(
"category == '" + context.getCategory() + "'"
);
}
return vectorStore.similaritySearch(request);
}
7.5 多 Vector Store 路由
在复杂项目中,可能需要同时使用多种 Vector Store:
@Configuration
public class MultiVectorStoreConfig {
@Bean
@Qualifier("milvusStore")
public VectorStore milvusVectorStore(EmbeddingModel embeddingModel,
MilvusServiceClient milvusClient) {
return new MilvusVectorStore(MilvusVectorStoreConfig.builder()
.withCollectionName("production_docs")
.withDimensions(1024)
.withIndexType(IndexType.HNSW)
.withMetricType(MetricType.COSINE)
.build(), embeddingModel, milvusClient, true);
}
@Bean
@Qualifier("redisStore")
public VectorStore redisVectorStore(EmbeddingModel embeddingModel,
JedisConnectionFactory connectionFactory) {
return new RedisVectorStore(RedisVectorStoreConfig.builder()
.withIndexName("cache_docs")
.withPrefix("cache:")
.build(), embeddingModel, connectionFactory, true);
}
}
@Service
public class VectorStoreRouter {
private final Map<String, VectorStore> stores;
public VectorStoreRouter(@Qualifier("milvusStore") VectorStore milvus,
@Qualifier("redisStore") VectorStore redis) {
this.stores = Map.of(
"production", milvus,
"cache", redis
);
}
public VectorStore getStore(String type) {
return stores.getOrDefault(type, stores.get("production"));
}
}
8. 常见问题与排查
8.1 向量维度不匹配
错误现象:dimension mismatch: expected 1024, got 768
原因:Embedding 模型配置的维度与 Vector Store 配置的维度不一致。
解决方案:
# 确保两处维度一致
spring:
ai:
dashscope:
embedding:
options:
dimensions: 1024 # ← Embedding 模型维度
vectorstore:
milvus:
embeddingDimension: 1024 # ← Vector Store 维度,必须一致
8.2 Milvus 连接失败
错误现象:ConnectException: Connection refused
排查步骤:
# 1. 检查 Milvus 容器状态
docker ps | grep milvus
# 2. 检查端口
docker port milvus-standalone
# 3. 检查 Milvus 日志
docker logs milvus-standalone
# 4. 测试连接
docker exec -it milvus-standalone milvus-util -host localhost -port 19530
8.3 搜索结果不相关
排查清单:
- 检查 Embedding 模型选择(text-embedding-v3 效果最好)
- 检查向量维度是否匹配
- 检查距离度量方式(推荐 COSINE)
- 调整相似度阈值(太低会返回不相关结果)
- 优化分块策略(块太大或太小都会影响精度)
- 检查是否有元数据过滤导致结果被过滤掉
8.4 Redis 索引创建失败
错误现象:ERR unknown command FT.CREATE
原因:Redis 版本不支持 RediSearch 模块。
解决方案:使用 Redis Stack 而非标准 Redis。
# 错误:标准 Redis 不支持向量检索
docker run redis:latest
# 正确:使用 Redis Stack
docker run redis/redis-stack:latest
8.5 pgvector 扩展未启用
错误现象:type "vector" does not exist
解决方案:
-- 连接 PostgreSQL 后执行
CREATE EXTENSION IF NOT EXISTS vector;
-- 验证
SELECT extname FROM pg_extension WHERE extname = 'vector';
总结
本文深入介绍了 Spring AI Alibaba 中 Vector Store 的集成方案,涵盖了 Milvus、Redis、PostgreSQL (pgvector) 三种主流向量数据库。
核心要点回顾:
- Spring AI 的统一抽象:无论使用哪种 Vector Store,API 接口保持一致,切换只需改配置
- Milvus:专业向量数据库,适合大规模生产环境,支持多种索引和分布式部署
- Redis:适合已有 Redis 基础设施、数据量不大的场景,毫秒级延迟
- pgvector:最轻量方案,一个扩展即可在 PostgreSQL 中获得向量检索能力
- 选型核心原则:根据数据量、现有基础设施、运维能力综合判断
- 性能优化关键:合理选择 Embedding 维度、分块策略、索引类型和搜索参数
下一篇预告:第11天——RAG 检索增强生成架构,我们将把 Embedding + Vector Store + ChatModel 串联起来,构建完整的 RAG 系统。敬请期待!