PGvector :在 Spring AI 中实现向量数据库存储与相似性搜索
PGvector :在 Spring AI 中实现向量数据库存储与相似性搜索
一、PGvector 概述与核心价值
1.1 什么是 PGvector
PGvector 是 PostgreSQL 的开源扩展,专为向量相似性搜索而设计。它允许开发者在 PostgreSQL 数据库中存储和搜索机器学习生成的嵌入(embeddings),支持精确和近似最近邻搜索。
💡 为什么选择 PGvector?无缝集成:作为 PostgreSQL 扩展,与现有数据库生态系统无缝协作ACID 合规:保持 PostgreSQL 的事务完整性功能丰富:支持多种距离度量和索引类型高性能:针对大规模向量搜索进行优化易用性:提供标准 SQL 接口,无需学习新查询语言
1.2 PGvector 的关键特性
| 特性 | 描述 | 优势 |
|---|---|---|
| 向量存储 | 支持多种向量类型(vector, halfvec, bit, sparsevec) | 适应不同精度和内存需求 |
| 距离度量 | L2、内积、余弦距离、L1、汉明距离、杰卡德距离 | 适用于不同场景的相似性度量 |
| 索引类型 | HNSW、IVFFlat | 平衡查询速度和召回率 |
| 元数据过滤 | 基于 JSON 的元数据过滤 | 精确控制检索结果 |
| 混合搜索 | 结合向量搜索和文本搜索 | 提高检索相关性 |
1.3 PGvector 与 Spring AI 的集成
Spring AI 通过 spring-ai-starter-vector-store-pgvector 提供了 PGvector 的开箱即用支持,使开发者能够轻松地将向量数据库集成到 RAG(检索增强生成)应用中。
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-pgvector</artifactId></dependency>二、PGvector 安装与配置
2.1 环境准备
2.1.1 前提条件
在使用 PGvector 之前,需要确保 PostgreSQL 实例已启用以下扩展:
CREATE EXTENSION IFNOTEXISTS vector;CREATE EXTENSION IFNOTEXISTS hstore;CREATE EXTENSION IFNOTEXISTS"uuid-ossp";如果没有参考 ✅ 宝塔 PostgreSQL 安装 contrib 扩展完整指南
2.2 手动创建向量存储表
在数据库中创建向量存储表(推荐在应用启动时由 Spring AI 自动创建):
CREATETABLEIFNOTEXISTS vector_store ( id uuid DEFAULT uuid_generate_v4()PRIMARYKEY, content text, metadata json, embedding vector(1536)-- 1536 是默认的嵌入维度);CREATEINDEXIFNOTEXISTS vector_index ON vector_store USING hnsw (embedding vector_cosine_ops);💡 重要提示:如果使用不同的嵌入维度,请将 1536 替换为实际的嵌入维度。PGvector 对 HNSW 索引最多支持 2000 个维度。三、Spring AI 集成 PGvector
3.1 依赖配置
在 Maven 项目中添加 PGvector 向量存储依赖:
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-pgvector</artifactId></dependency>3.2 自动配置
Spring AI 提供了自动配置功能,通过 application.yml 配置 PGvector:
spring:datasource:url: jdbc:postgresql://localhost:5432/postgres username: postgres password: postgres ai:vectorstore:pgvector:index-type: HNSW distance-type: COSINE_DISTANCE dimensions:1536max-document-batch-size:10000💡 关键配置说明:initialize-schema: true:在应用启动时自动创建所需的数据库表dimensions:嵌入维度(如果未指定,将从 EmbeddingModel 获取)index-type:索引类型(HNSW、IVFFlat、NONE)distance-type:距离类型(COSINE_DISTANCE、EUCLIDEAN_DISTANCE、NEGATIVE_INNER_PRODUCT)
3.3 手动配置(高级用法)
不使用 Spring Boot 自动配置时,可以手动创建 PgVectorStore:
@ConfigurationpublicclassPgVectorConfig{@BeanpublicVectorStorevectorStore(JdbcTemplate jdbcTemplate,EmbeddingModel embeddingModel){returnPgVectorStore.builder(jdbcTemplate, embeddingModel).dimensions(1536)// 默认为模型维度.distanceType(COSINE_DISTANCE).indexType(HNSW).initializeSchema(true)// 启用自动表创建.schemaName("public").vectorTableName("vector_store")// 哪一个数据库.maxDocumentBatchSize(10000).build();}}3.4 依赖项要求
手动配置时,需要添加以下依赖:
<!-- Spring Boot JDBC 支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- PostgreSQL JDBC 驱动 --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><scope>runtime</scope></dependency><!-- Spring AI PGvector Store --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-pgvector-store</artifactId></dependency>四、向量存储操作实践
4.1 存储文档
@Resource@Qualifier("pgVectorStore")privateVectorStore vectorStore;/** * 文档嵌入与存储 */publicvoidstoreDocuments(){// 4.1.1 创建文档对象List<Document> documents =List.of(newDocument("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!",Map.of("author","john","article_type","blog","meta1","meta1")),newDocument("The World is Big and Salvation Lurks Around the Corner",Map.of("author","jill","article_type","news","category","philosophy")),newDocument("You walk forward facing the past and you turn back toward the future.",Map.of("author","john","article_type","blog","meta2","meta2","category","philosophy")),newDocument("Spring Framework is a powerful Java framework for building enterprise applications.",Map.of("author","alice","article_type","tutorial","category","programming")),newDocument("Artificial Intelligence is transforming the tech industry.",Map.of("author","bob","article_type","article","category","ai")));// 4.1.2 存储文档到向量存储System.out.println("开始存储文档到向量存储..."); vectorStore.add(documents);System.out.println("文档存储完成!");// 验证存储List<Document> results = vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());System.out.println("\n存储验证 - 检索结果:"); results.forEach(doc ->{System.out.println("内容: "+Objects.requireNonNull(doc.getText()).substring(0,Math.min(50, doc.getText().length()))+"...");System.out.println("元数据: "+ doc.getMetadata());System.out.println("---");});}

4.2 基本相似性搜索
/** * 基本相似性搜索 */publicList<Document>basicSimilaritySearch(){System.out.println("\n执行基本相似性搜索...");SearchRequest searchRequest =SearchRequest.builder().query("Spring").topK(3).similarityThreshold(0.5)// 相似度阈值(0-1之间).build();List<Document> results = vectorStore.similaritySearch(searchRequest);System.out.println("基本搜索 - 查询 'Spring' 的结果:"); results.forEach(doc ->{System.out.println("内容: "+ doc.getText());System.out.println("元数据: "+ doc.getMetadata());System.out.println("相似度: "+ doc.getMetadata().get("distance"));System.out.println("---");});return results;}
4.3 使用元数据过滤 - 文本表达式
/** * 使用元数据过滤 - 文本表达式 */publicList<Document>searchWithMetadataFilter(){System.out.println("\n使用元数据过滤搜索...");SearchRequest searchRequest =SearchRequest.builder().query("technology").topK(5).similarityThreshold(0.3)// 注意:具体过滤表达式语法取决于向量存储实现// 这里使用 Spring AI 的标准过滤语法.filterExpression("author in ['john', 'jill'] && article_type == 'blog'").build();// 方法1: 使用文本表达式语言进行过滤List<Document> results = vectorStore.similaritySearch(searchRequest);System.out.println("元数据过滤搜索 - 文本表达式结果:"); results.forEach(doc ->{System.out.println("内容: "+ doc.getText());System.out.println("元数据: "+ doc.getMetadata());System.out.println("---");});return results;}
4.4 使用元数据过滤 - Filter.Expression DSL
publicList<Document>searchWithProgrammaticFilter(){System.out.println("\n使用编程式元数据过滤搜索...");// 方法2: 使用 Filter.Expression DSL 进行编程式过滤FilterExpressionBuilder b =newFilterExpressionBuilder();List<Document> results = vectorStore.similaritySearch(SearchRequest.builder().query("technology").topK(5).similarityThreshold(0.3).filterExpression(b.and( b.in("author","john","jill"), b.eq("article_type","blog")).build()).build());System.out.println("编程式过滤搜索结果:"); results.forEach(doc ->{System.out.println("内容: "+ doc.getText());System.out.println("元数据: "+ doc.getMetadata());System.out.println("---");});return results;}
4.5 高级搜索示例:组合查询和过滤
publicList<Document>advancedSearchExample(){System.out.println("\n高级搜索示例...");// 创建复杂过滤条件FilterExpressionBuilder builder =newFilterExpressionBuilder();// (author == 'john' || author == 'jill') && category == 'philosophy'Filter.Expression filter = builder.and( builder.or( builder.eq("author","john"), builder.eq("author","jill")), builder.eq("category","philosophy")).build();List<Document> results = vectorStore.similaritySearch(SearchRequest.builder().query("life and future").topK(10).similarityThreshold(0.0).filterExpression(filter).build());System.out.println("高级搜索结果:"); results.forEach(doc ->{System.out.println("内容: "+ doc.getText());System.out.println("元数据: "+ doc.getMetadata());System.out.println("---");});return results;}
4.6 删除特定文档(根据元数据)

publicvoiddeleteDocumentsByMetadata(){System.out.println("\n删除 author 为 'john' 的文档...");// 注意:删除功能可能因向量存储实现而异// PGvector 通常支持根据元数据过滤删除// 先搜索要删除的文档FilterExpressionBuilder builder =newFilterExpressionBuilder();Filter.Expression filter = builder.eq("author","john").build();List<Document> docsToDelete = vectorStore.similaritySearch(SearchRequest.builder().query("").topK(100).filterExpression(filter).build());if(!docsToDelete.isEmpty()){// 提取文档ID(PGvector中id在metadata中)List<String> ids = docsToDelete.stream().map(Document::getId).filter(Objects::nonNull).toList();System.out.println("找到 "+ ids.size()+" 个要删除的文档");// 删除文档(具体方法取决于VectorStore实现) vectorStore.delete(ids);}}五、PGvector 配置参数详解
5.1 关键配置属性
| 属性 | 描述 | 默认值 | 适用场景 |
|---|---|---|---|
index-type | 最近邻搜索索引类型 | HNSW | 需要高性能检索时 |
distance-type | 搜索距离类型 | COSINE_DISTANCE | 向量已归一化时 |
dimensions | 嵌入维度 | 从 EmbeddingModel 获取 | 与嵌入模型一致 |
remove-existing-vector-store-table | 启动时删除现有表 | false | 重置数据库时 |
initialize-schema | 是否初始化 schema | false | 首次使用时 |
schema-name | 向量存储 schema 名称 | public | 多 schema 环境 |
table-name | 向量存储表名称 | vector_store | 自定义表名 |
schema-validation | 启用 schema 和表名验证 | false | 安全敏感环境 |
max-document-batch-size | 单批处理的最大文档数 | 10000 | 大批量数据导入 |
5.2 索引类型对比
| 索引类型 | 构建时间 | 查询性能 | 内存使用 | 适用场景 |
|---|---|---|---|---|
| HNSW | 较慢 | 优秀 | 较高 | 高性能要求,数据量大 |
| IVFFlat | 快 | 一般 | 较低 | 数据量小,内存有限 |
| NONE | 无 | 一般 | 低 | 测试环境,小数据量 |
💡 HNSW 与 IVFFlat 选择建议:对于 100K+ 向量,选择 HNSW对于 10K-100K 向量,选择 IVFFlat对于 <10K 向量,使用 NONE
5.3 距离类型选择
| 距离类型 | 适用场景 | 性能 | 说明 |
|---|---|---|---|
| COSINE_DISTANCE | 向量已归一化 | 优秀 | 适用于大多数嵌入模型 |
| EUCLIDEAN_DISTANCE | 向量未归一化 | 一般 | 需要精确距离 |
| NEGATIVE_INNER_PRODUCT | 向量已归一化 | 优秀 | 与 COSINE_DISTANCE 等效 |
💡 重要提示:如果向量已经归一化到长度 1(如 OpenAI 嵌入),使用 COSINE_DISTANCE 或 NEGATIVE_INNER_PRODUCT 能获得最佳性能。
六、高级用法与最佳实践
6.1 性能优化技巧
6.1.1 索引优化
// HNSW 索引优化参数 CREATE INDEX ON vector_store USING hnsw (embedding vector_cosine_ops) WITH (m =16, ef_construction =64);- m:每层的最大连接数(默认 16)
- ef_construction:构建图时的动态候选列表大小(默认 64)
💡 优化建议:对于高召回率要求,提高 ef_construction;对于快速构建,降低它。6.1.2 查询优化
// 设置查询时的动态候选列表大小 SET hnsw.ef_search =100;- ef_search:查询时的动态候选列表大小(默认 40)
- 值越大,召回率越高,但速度越慢
💡 优化建议:对于关键查询,设置 ef_search=100;对于普通查询,保持默认。6.1.3 批量处理优化
// 批量添加文档(使用最大批次大小) vectorStore.add(documents,10000);// 10000 是 maxDocumentBatchSize💡 优化建议:使用 maxDocumentBatchSize 配置值作为批量大小,避免内存溢出。6.2 安全最佳实践
6.2.1 数据库权限管理
-- 为应用用户创建最小权限CREATE ROLE app_user;GRANTCONNECTONDATABASE postgres TO app_user;GRANTUSAGEONSCHEMApublicTO app_user;GRANTSELECT,INSERT,UPDATE,DELETEONTABLE vector_store TO app_user;6.2.2 元数据过滤安全
// 在应用层验证过滤表达式if(isValidFilterExpression(filterExpression)){// 执行查询}💡 安全建议:不要直接使用用户输入的过滤表达式,进行验证和清理。
6.2.3 启用 schema 验证
spring:ai:vectorstore:pgvector:schema-validation:true💡 安全建议:在生产环境中始终启用 schema-validation,防止 SQL 注入。6.3 与 RAG 流程集成
6.3.1 使用 QuestionAnswerAdvisor
@BeanpublicAdvisorquestionAnswerAdvisor(VectorStore vectorStore){returnQuestionAnswerAdvisor.builder(vectorStore).searchRequest(SearchRequest.builder().similarityThreshold(0.7).topK(5).build()).build();}6.3.2 使用 RetrievalAugmentationAdvisor
@BeanpublicAdvisorretrievalAugmentationAdvisor(VectorStore vectorStore){returnRetrievalAugmentationAdvisor.builder().documentRetriever(VectorStoreDocumentRetriever.builder().vectorStore(vectorStore).similarityThreshold(0.7).topK(5).build()).queryAugmenter(ContextualQueryAugmenter.builder().allowEmptyContext(true).build()).build();}6.3.3 RAG 流程工作流程
- 用户输入:用户提出问题
- 查询处理:使用 QueryTransformer 优化查询
- 文档检索:使用 VectorStoreDocumentRetriever 检索相关文档
- 上下文增强:使用 ContextualQueryAugmenter 增强查询
- 生成响应:使用 ChatModel 生成最终响应
七、常见问题与解决方案
7.1 问题:查询性能差
可能原因:
- 没有使用合适的索引
- 索引参数未优化
解决方案:
- 确保已创建 HNSW 或 IVFFlat 索引
设置查询参数:
SET hnsw.ef_search =100;优化索引参数:
CREATEINDEXON vector_store USING hnsw (embedding vector_cosine_ops)WITH(m =16, ef_construction =64);7.2 问题:向量维度不匹配
可能原因:
- 数据库表中的嵌入维度与 EmbeddingModel 不一致
解决方案:
- 确认 EmbeddingModel 的维度
或在 Spring AI 配置中指定维度:
spring:ai:vectorstore:pgvector:dimensions:1536在数据库表中设置正确的维度:
CREATETABLE vector_store ( id uuid DEFAULT uuid_generate_v4()PRIMARYKEY, content text, metadata json, embedding vector(1536)-- 确保与模型一致);7.3 问题:元数据过滤无效
可能原因:
- 过滤表达式格式不正确
- 元数据存储格式不一致
解决方案:
- 检查 PostgreSQL 日志,查看实际执行的 SQL 语句
使用正确的过滤表达式:
.filterExpression("key1 == 'value1' && key2 == 'value2'")确认元数据格式:
newDocument("内容",Map.of("key1","value1","key2","value2"));7.4 问题:内存不足
可能原因:
- HNSW 索引构建时内存不足
- 查询时内存不足
八、总结
关键要点回顾
- PGvector :是 PostgreSQL 的开源向量扩展,支持高效向量相似性搜索
- Spring AI :通过
spring-ai-starter-vector-store-pgvector提供了开箱即用的集成 - 向量存储 :需要正确配置维度、距离类型和索引类型
- 元数据过滤 :提供了强大的结果筛选能力
- 性能优化 :通过调整索引和查询参数实现