欢迎光临
我们一直在努力

Spring AI 整合 PostgreSQL pgvector 向量检索全流程

Spring AI 整合 PostgreSQL pgvector 向量检索全流程

封面信息图

知识库问答、商品智能检索与语义推荐系统在企业级业务中全面落地,向量检索能力已经成为现代应用架构的标准组件。面对向量检索需求,很多团队的第一反应是直接采购或自建专用的向量数据库(如 Milvus、Pinecone、Qdrant)。然而在真实生产环境中,引入一套全新的存储中间件意味着额外的运维成本、监控链路、备份容灾方案以及双写一致性难题。

如果业务系统本身已经依赖 PostgreSQL,直接利用 PostgreSQL 的扩展插件 pgvector 配合 Spring AI,能够以极低的架构侵入性和硬件成本,在同一套关系型数据库中实现“业务关系数据 + 向量特征数据”的混合存储与事务一致性检索。


选型账本:为什么选择 pgvector

在决定是否引入专用向量数据库前,我们先梳理一组工程维度的对比:

维度专用向量数据库(如 Milvus)PostgreSQL + pgvector
数据一致性 业务主库与向量库双写,需借助事务消息或 CDC 保证最终一致 天然支持 ACID 事务,业务字段与向量在同一张表或同库级联更新
混合过滤查询 多数仅支持简单的标量过滤,复杂的关联联表(JOIN)性能差或不支持 支持原生 SQL、多表 JOIN、JSONB 嵌套查询以及精确索引过滤
运维门槛 需独立部署集群、协调 Etcd/MinIO/Pulsar 等依赖,运维链路长 复用现有 RDS/PostgreSQL 实例及高可用、主从、备份容灾体系
性能吞吐 百万到千万级以上超大规模向量检索性能出众 百万级以下向量检索在 HNSW 索引下延迟保持在毫秒级,满足多数场景

如果应用的核心诉求是向量规模在千万级以内、强依赖业务元数据权限过滤、且希望避免跨库分布式事务,PostgreSQL + pgvector 是当前 ROI(投入产出比)极高且架构极度干净的选择。


数据库与 pgvector 插件初始化

1. 开启扩展插件

确保 PostgreSQL 实例版本在 15+(推荐 PG 16,HNSW 索引性能大幅提升),执行以下 SQL 启用插件:

— 启用向量扩展
CREATE EXTENSION IF NOT EXISTS vector;

2. 向量数据表结构设计

Spring AI 默认的 PgVectorStore 依赖标准的数据表结构。生产环境中,我们通常为知识库或业务实体设计如下表结构:

CREATE TABLE IF NOT EXISTS vector_store (
id VARCHAR(36) PRIMARY KEY,
content TEXT,
metadata JSONB,
embedding VECTOR(1536) — 维度需与 Embedding 模型输出严格对齐(如 text-embedding-3-small 为 1536 维)
);

— 为元数据字段创建 GIN 索引,加速混合过滤
CREATE INDEX IF NOT EXISTS idx_vector_store_metadata ON vector_store USING GIN (metadata);

3. 索引选型:HNSW 与 IVFFlat

pgvector 支持两种主流近似最近邻(ANN)索引:

  • IVFFlat(倒排文件扁平索引):构建速度快,内存消耗低,但检索召回率和速度在数据频繁更新时容易退化,且必须在表中有一定量数据后才能建立高质量索引。
  • HNSW(分层导航小世界图索引):构建耗时稍长,内存占用较高,但检索速度极快,召回率高,且支持实时增量插入。

在生产业务中,推荐优先使用 HNSW 索引,并采用余弦距离(Cosine Distance)计算:

— 使用 HNSW 构建余弦相似度索引
— m: 每个节点的最大连接数(默认 16,推荐 16-64)
— ef_construction: 构造索引时的搜索候选集大小(默认 64,推荐 64-128)
CREATE INDEX IF NOT EXISTS idx_vector_store_embedding_hnsw
ON vector_store USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);


Spring AI 依赖与核心配置

使用 Spring Boot 3.3+ 与 Spring AI 1.0.0-M2+ 版本,在 pom.xml 中引入必要依赖:

<dependencies>
<!– Spring AI PostgreSQL pgvector Starter –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>

<!– 选用 OpenAI 或自建 Ollama / DashScope 兼容的 Embedding 模型 –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

<!– PostgreSQL 驱动 –>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

在 application.yml 中配置数据库连接、向量表属性与索引参数:

spring:
datasource:
url: jdbc:postgresql://127.0.0.1:5432/knowledge_base?reWriteBatchedInserts=true
username: app_user
password: app_secure_password
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 20000

ai:
openai:
api-key: ${OPENAI_API_KEY:sk-placeholder}
base-url: ${OPENAI_BASE_URL:https://api.openai.com}
embedding:
options:
model: text-embedding-3-small
vectorstore:
pgvector:
table-name: vector_store
schema-name: public
dimensions: 1536
distance-type: COSINE_DISTANCE
index-type: HNSW
initialize-schema: false # 生产环境严禁自动建表,由 Flyway/Liquibase 统一管理


核心业务代码实现

1. VectorStore 配置与 Bean 定制

在某些定制化场景下,需要对 PgVectorStore 注入自定义的 JdbcTemplate 或设置特定的查询参数:

package com.example.knowledge.config;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.PgVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class VectorStoreConfig {

@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {
return PgVectorStore.builder(jdbcTemplate, embeddingModel)
.dimensions(1536)
.distanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE)
.indexType(PgVectorStore.PgIndexType.HNSW)
.tableName("vector_store")
.schemaName("public")
.initializeSchema(false)
.build();
}
}

2. 文档切片与向量入库服务

编写知识库录入服务,负责文本清洗、分块(Chunking)、元数据附加与批量写入:

package com.example.knowledge.service;

import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class DocumentIndexService {

private final VectorStore vectorStore;

public DocumentIndexService(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}

/**
* 批量导入知识库片段
* @param docId 业务文档唯一ID
* @param tenantId 租户ID(用于多租户数据隔离)
* @param chunks 文本切片列表
*/
@Transactional(rollbackFor = Exception.class)
public void indexDocumentChunks(String docId, String tenantId, List<String> chunks) {
List<Document> documents = chunks.stream().map(chunk -> {
// 为每个分片构建唯一的 Document,并注入多租户与溯源元数据
Map<String, Object> metadata = Map.of(
"docId", docId,
"tenantId", tenantId,
"indexedAt", System.currentTimeMillis()
);
return new Document(UUID.randomUUID().toString(), chunk, metadata);
}).toList();

// 内部自动调用 EmbeddingModel 向量化并批量写入 PostgreSQL
vectorStore.accept(documents);
}
}

3. 混合过滤与相似度检索

在实际业务中,检索请求必须携带租户隔离条件与相似度阈值过滤:

package com.example.knowledge.service;

import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class KnowledgeRetrievalService {

private final VectorStore vectorStore;

public KnowledgeRetrievalService(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}

/**
* 相似度检索并执行租户元数据过滤
* @param query 用户问题
* @param tenantId 租户隔离标识
* @param topK 返回最大条数
* @param minSimilarity 最小相似度阈值 (0.0 ~ 1.0)
*/
public List<Document> retrieveRelevantChunks(String query, String tenantId, int topK, double minSimilarity) {
FilterExpressionBuilder b = new FilterExpressionBuilder();

SearchRequest request = SearchRequest.builder()
.query(query)
.topK(topK)
.similarityThreshold(minSimilarity)
// 构建 SQL JSONB 过滤条件:metadata->>'tenantId' == tenantId
.filterExpression(b.eq("tenantId", tenantId).build())
.build();

return vectorStore.similaritySearch(request);
}
}


生产避坑与参数调优

1. HNSW 查询检索深度(ef_search)

默认情况下,pgvector 的 hnsw.ef_search 参数为 40。如果发现检索召回率不足,或者在复杂混合过滤时漏掉相关结果,可以在查询连接中调大该参数以换取更高召回率:

— 在会话级别或全局配置中调优
SET hnsw.ef_search = 100;

在 Spring Boot 中,可以通过 HikariCP 的 connection-init-sql 进行连接初始化设置:

spring:
datasource:
hikari:
connection-init-sql: "SET hnsw.ef_search = 100; SET work_mem = '64MB';"

2. 内存与维护参数(work_mem 与 maintenance_work_mem)

构建 HNSW 索引极度消耗内存。如果向量数据量达到几十万条,默认的 maintenance_work_mem(通常 64MB)会导致索引构建极其缓慢甚至失败。

  • 构建索引前:临时提升 SET maintenance_work_mem = '2GB';。
  • 查询阶段:向量检索涉及内存向量距离计算,适当调大 work_mem 到 32MB~64MB 能够避免中间计算溢出到磁盘临时文件。

3. Embedding 接口超时与批量限流

vectorStore.accept(documents) 在底层会向外部 Embedding 接口发送 HTTP 批量请求。当单次写入上千条切片时,极易触发模型厂商的 RPM/TPM 限流或超时断连。工程对策:在业务层对切片列表按 50~100 条进行分批(Partition),并使用重试组件(如 Resilience4j 或 Spring Retry)包装向量化调用。


总结

利用 Spring AI 与 PostgreSQL pgvector 的整合,我们不需要为了向量检索而引入额外的重型存储组件。基于关系型数据库现有的事务支持、JSONB 元数据过滤以及 HNSW 索引,可以在极低的硬件成本与运维复杂度下,快速搭建稳定高可用的企业级知识库与检索增强系统。在中轻量级 AI 落地场景中,这是一套兼顾开发效率、架构纯粹性与运行稳定性的方案。

赞(0)
未经允许不得转载:171主机测评 » Spring AI 整合 PostgreSQL pgvector 向量检索全流程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址