SpringAI2.0 向量存储生态:Redis、Amazon S3 与 Bedrock Knowledge Base 集成
前言:向量存储的多元化选择
在 RAG(检索增强生成)系统中,向量存储是核心组件之一。Spring AI 2.0 大幅扩展了向量存储的生态,新增了多个后端支持,包括 Redis、Amazon S3、Bedrock Knowledge Base 等。
作为架构师,我在多个企业项目中见证过向量存储选型的纠结:选择 Redis 还是 PostgreSQL?使用专用的向量数据库如 Milvus,还是利用现有基础设施如 Amazon S3?Spring AI 2.0 通过统一的 API 让我们可以轻松切换不同的向量存储后端,避免了厂商锁定。
本文将深入探讨 Spring AI 2.0 的向量存储生态,重点介绍 Redis Vector Store 的语义缓存、Amazon S3 作为向量存储后端的实现,以及 Bedrock Knowledge Base 的集成方案。
一、Redis Vector Store 语义缓存
1.1 Redis 作为向量存储的优势
Redis 是一个内存数据结构存储系统,Spring AI 2.0 将其升级为全功能的向量存储后端。选择 Redis 作为向量存储的优势:
- 高性能:基于内存,响应速度快
- 成熟稳定:生产环境广泛使用
- 丰富功能:支持持久化、集群、主从复制
- 语义缓存:天然的缓存层,适合 RAG 场景
- 多数据类型:同时支持向量数据和元数据存储
1.2 Redis Vector Store 配置
基础配置
yaml
# application.yml
spring:
data:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
ai:
vectorstore:
redis:
index: ai-documents
prefix: doc:
initialize-schema: true
Java 配置
java
@Configuration
public class RedisVectorStoreConfig {
@Bean
public RedisVectorStore redisVectorStore(
RedisConnectionFactory connectionFactory,
EmbeddingModel embeddingModel
) {
RedisVectorStoreConfig config = RedisVectorStoreConfig.builder()
.withIndexName("ai-documents")
.withPrefix("doc:")
.withInitializeSchema(true)
.build();
return new RedisVectorStore(connectionFactory, embeddingModel, config);
}
}
1.3 语义缓存架构
语义缓存是 Redis Vector Store 的核心特性,它可以缓存常见问题的答案,避免重复调用 AI 模型。
用户问题 → 语义相似度检索 → Redis 缓存
↓
命中:直接返回
↓
未命中:调用 AI 模型 → 存入缓存
基础语义缓存实现
java
@Service
public class SemanticCacheService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
public SemanticCacheService(
VectorStore vectorStore,
ChatClient chatClient
) {
this.vectorStore = vectorStore;
this.chatClient = chatClient;
}
// 带语义缓存的问答
public String askWithCache(String question) {
// 1. 先在缓存中查找相似问题
SearchRequest request = SearchRequest.query(question)
.withTopK(1)
.withSimilarityThreshold(0.85); // 高相似度阈值
List<Document> cachedDocs = vectorStore.similaritySearch(request);
// 2. 如果找到缓存,直接返回
if (!cachedDocs.isEmpty()) {
Document cached = cachedDocs.get(0);
log.info("命中语义缓存,相似度:{}",
cached.getMetadata().get("similarity"));
return cached.getContent();
}
// 3. 未命中缓存,调用 AI 模型
String answer = chatClient.prompt()
.user(question)
.call()
.content();
// 4. 将问答对存入缓存
Document cacheDoc = new Document(answer, Map.of(
"question", question,
"cached_at", Instant.now(),
"type", "cache"
));
vectorStore.add(List.of(cacheDoc));
return answer;
}
}
高级语义缓存策略
java
@Service
public class AdvancedSemanticCacheService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
private final Cache<String, String> exactMatchCache; // 精确匹配缓存
// 多层缓存策略
public String askWithMultiLevelCache(String question) {
// 第一层:精确匹配缓存
String exactMatch = exactMatchCache.getIfPresent(question);
if (exactMatch != null) {
log.info("命中精确匹配缓存");
return exactMatch;
}
// 第二层:语义相似度缓存
SearchRequest request = SearchRequest.query(question)
.withTopK(3)
.withSimilarityThreshold(0.9); // 非常高的相似度
List<Document> semanticCache = vectorStore.similaritySearch(request);
if (!semanticCache.isEmpty()) {
Document bestMatch = semanticCache.get(0);
double similarity = (double) bestMatch.getMetadata()
.getOrDefault("similarity", 0.0);
// 如果相似度足够高,返回缓存答案
if (similarity >= 0.95) {
log.info("命中语义缓存,相似度:{}", similarity);
exactMatchCache.put(question, bestMatch.getContent());
return bestMatch.getContent();
}
}
// 第三层:调用 AI 模型
String answer = chatClient.prompt()
.user(question)
.call()
.content();
// 存入多层缓存
exactMatchCache.put(question, answer);
Document cacheDoc = new Document(answer, Map.of(
"question", question,
"cached_at", Instant.now(),
"type", "semantic_cache"
));
vectorStore.add(List.of(cacheDoc));
return answer;
}
// 缓存预热
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨 2 点
public void warmUpCache() {
log.info("开始缓存预热");
List<String> frequentQuestions = getFrequentQuestions();
for (String question : frequentQuestions) {
try {
askWithMultiLevelCache(question);
Thread.sleep(100); // 避免过快调用
} catch (Exception e) {
log.error("缓存预热失败:{}", question, e);
}
}
log.info("缓存预热完成");
}
private List<String> getFrequentQuestions() {
// 从数据库或日志中获取高频问题
return List.of();
}
}
1.4 Redis 集群配置
yaml
# application.yml
spring:
data:
redis:
cluster:
max-redirects: 3
nodes:
- redis-node1:6379
- redis-node2:6379
- redis-node3:6379
lettuce:
pool:
max-active: 16
max-idle: 16
min-idle: 8
java
@Configuration
public class RedisClusterConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(
Arrays.asList(
new RedisNode("redis-node1", 6379),
new RedisNode("redis-node2", 6379),
new RedisNode("redis-node3", 6379)
)
);
clusterConfig.setMaxRedirects(3);
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.poolConfig(redisPoolConfig())
.build();
return new LettuceConnectionFactory(clusterConfig, clientConfig);
}
private GenericObjectPoolConfig<RedisClient> redisPoolConfig() {
GenericObjectPoolConfig<RedisClient> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(16);
config.setMaxIdle(16);
config.setMinIdle(8);
return config;
}
}
二、Amazon S3 作为向量存储后端
2.1 S3 作为向量存储的优势
Amazon S3 是对象存储服务,Spring AI 2.0 将其扩展为向量存储后端:
- 持久化存储:数据持久可靠,支持版本控制
- 无限扩展:存储容量无限制
- 成本效益:按使用量付费,适合大规模数据
- 全球可用:多区域部署,就近访问
- 生命周期管理:自动归档和删除过期数据
2.2 S3 Vector Store 配置
基础配置
yaml
# application.yml
spring:
ai:
aws:
s3:
region: us-east-1
access-key-id: ${AWS_ACCESS_KEY_ID}
secret-access-key: ${AWS_SECRET_ACCESS_KEY}
bucket-name: ai-documents
path-prefix: embeddings/
vectorstore:
s3:
enabled: true
Java 配置
java
@Configuration
public class S3VectorStoreConfig {
@Bean
public S3VectorStore s3VectorStore(
EmbeddingModel embeddingModel,
S3Client s3Client,
S3VectorStoreConfig config
) {
config = S3VectorStoreConfig.builder()
.withBucketName("ai-documents")
.withPathPrefix("embeddings/")
.build();
return new S3VectorStore(embeddingModel, s3Client, config);
}
@Bean
public S3Client s3Client() {
return S3Client.builder()
.region(Region.US_EAST_1)
.credentialsProvider(
DefaultCredentialsProvider.create()
)
.build();
}
}
2.3 非结构化数据管理
S3 不仅可以存储向量,还可以管理原始文档。
java
@Service
public class S3DocumentService {
private final S3Client s3Client;
private final VectorStore vectorStore;
private final EmbeddingModel embeddingModel;
// 上传文档并生成嵌入
public void uploadAndIndex(
InputStream inputStream,
String fileName,
Map<String, Object> metadata
) {
// 1. 上传原始文档到 S3
String s3Key = "documents/" + fileName;
PutObjectRequest putRequest = PutObjectRequest.builder()
.bucket("ai-documents")
.key(s3Key)
.build();
s3Client.putObject(putRequest,
RequestBody.fromInputStream(inputStream, inputStream.available()));
// 2. 读取文档内容
String content = readDocumentContent(inputStream);
// 3. 生成嵌入
List<Float> embedding = embeddingModel.embed(content);
// 4. 存储向量数据
metadata.put("s3_key", s3Key);
metadata.put("s3_bucket", "ai-documents");
metadata.put("file_name", fileName);
Document document = new Document(content, metadata);
vectorStore.add(List.of(document));
}
// 从 S3 检索文档内容
public String getDocumentContent(String s3Key) {
GetObjectRequest getRequest = GetObjectRequest.builder()
.bucket("ai-documents")
.key(s3Key)
.build();
try (InputStream is = s3Client.getObject(getRequest)) {
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException("读取 S3 文档失败", e);
}
}
// 批量删除
public void batchDelete(List<String> s3Keys) {
List<ObjectIdentifier> objects = s3Keys.stream()
.map(key -> ObjectIdentifier.builder().key(key).build())
.toList();
DeleteObjectsRequest deleteRequest = DeleteObjectsRequest.builder()
.bucket("ai-documents")
.delete(Delete.builder()
.objects(objects)
.build())
.build();
s3Client.deleteObjects(deleteRequest);
}
}
2.4 S3 生命周期管理
java
@Service
public class S3LifecycleService {
private final S3Client s3Client;
// 设置生命周期规则
public void setupLifecycleRules() {
// 30 天后迁移到标准 IA
StandardTransition standardTransition = StandardTransition.builder()
.days(30)
.storageClass(StorageClass.STANDARD_IA)
.build();
// 90 天后迁移到 Glacier
GlacierTransition glacierTransition = GlacierTransition.builder()
.days(90)
.storageClass(StorageClass.GLACIER)
.build();
// 180 天后删除
LifecycleRuleFilter filter = LifecycleRuleFilter.builder()
.prefix("embeddings/")
.build();
LifecycleRule rule = LifecycleRule.builder()
.id("ai-documents-lifecycle")
.filter(filter)
.transition(standardTransition)
.transition(glacierTransition)
.expiration(Expiration.builder().days(180).build())
.status(ExpirationStatus.ENABLED)
.build();
BucketLifecycleConfiguration lifecycleConfig = BucketLifecycleConfiguration
.builder()
.rules(rule)
.build();
s3Client.putBucketLifecycleConfiguration(
PutBucketLifecycleConfigurationRequest.builder()
.bucket("ai-documents")
.lifecycleConfiguration(lifecycleConfig)
.build()
);
}
}
三、Bedrock Knowledge Base 集成
3.1 Bedrock Knowledge Base 概述
Amazon Bedrock Knowledge Base 是 AWS 提供的托管式 RAG 服务,它集成了向量搜索和知识库管理功能。
优势:
- 托管服务:无需管理底层基础设施
- 与 AWS 生态集成:与 S3、OpenSearch 等无缝集成
- 多模型支持:支持 Amazon Titan、Claude 等模型
- 高可用性:AWS 保证的高可用和持久性
3.2 Bedrock Knowledge Base 配置
yaml
# application.yml
spring:
ai:
bedrock:
knowledge-base:
region: us-east-1
knowledge-base-id: ${BEDROCK_KB_ID}
model-id: amazon.titan-embed-text-v2
java
@Configuration
public class BedrockKnowledgeBaseConfig {
@Bean
public BedrockKnowledgeBase bedrockKnowledgeBase(
BedrockRuntimeClient bedrockClient,
BedrockAgentRuntimeClient agentRuntimeClient
) {
return BedrockKnowledgeBase.builder()
.knowledgeBaseId(System.getenv("BEDROCK_KB_ID"))
.modelArn("arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2")
.bedrockClient(bedrockClient)
.agentRuntimeClient(agentRuntimeClient)
.build();
}
@Bean
public BedrockRuntimeClient bedrockClient() {
return BedrockRuntimeClient.builder()
.region(Region.US_EAST_1)
.build();
}
@Bean
public BedrockAgentRuntimeClient agentRuntimeClient() {
return BedrockAgentRuntimeClient.builder()
.region(Region.US_EAST_1)
.build();
}
}
3.3 Bedrock 知识库操作
java
@Service
public class BedrockKnowledgeBaseService {
private final BedrockKnowledgeBase knowledgeBase;
// 添加文档到知识库
public void addDocumentToKB(
String documentTitle,
String documentContent
) {
IngestionJob job = IngestionJob.builder()
.knowledgeBaseId(knowledgeBase.getKnowledgeBaseId())
.dataSourceId("s3-data-source")
.clientToken(UUID.randomUUID().toString())
.build();
StartIngestionJobRequest request = StartIngestionJobRequest.builder()
.ingestionJob(job)
.build();
knowledgeBase.startIngestionJob(request);
log.info("开始摄取文档到知识库:{}", documentTitle);
}
// 从知识库检索
public List<Document> retrieveFromKB(
String query,
int maxResults
) {
RetrieveRequest request = RetrieveRequest.builder()
.knowledgeBaseId(knowledgeBase.getKnowledgeBaseId())
.retrievalQuery(RetrievalQuery.builder()
.text(query)
.build())
.retrievalConfiguration(RetrievalConfiguration.builder()
.vectorSearchConfiguration(VectorSearchConfiguration.builder()
.numberOfResults(maxResults)
.build())
.build())
.build();
RetrieveResponse response = knowledgeBase.retrieve(request);
return response.retrievalResults().stream()
.map(result -> new Document(
result.content().text(),
Map.of(
"score", result.score(),
"source", result.location().s3Location().uri()
)
))
.toList();
}
// 使用知识库生成答案
public String generateAnswerWithKB(String query) {
RetrieveAndGenerateRequest request = RetrieveAndGenerateRequest.builder()
.knowledgeBaseId(knowledgeBase.getKnowledgeBaseId())
.inputText(query)
.generationConfiguration(GenerationConfiguration.builder()
.guardrailConfiguration(GuardrailConfiguration.builder()
.guardrailIdentifier("content-filter")
.guardrailVersion("1")
.build())
.build())
.build();
RetrieveAndGenerateResponse response =
knowledgeBase.retrieveAndGenerate(request);
return response.generation().text();
}
}
四、Infinispan 分布式缓存向量存储
4.1 Infinispan 简介
Infinispan 是一个开源的数据网格平台,提供分布式缓存和 NoSQL 数据存储功能。Spring AI 2.0 支持 Infinispan 作为向量存储后端。
优势:
- 分布式缓存:支持集群部署
- 高性能:内存存储,响应快速
- 可扩展:水平扩展能力强
- 持久化:支持多种持久化策略
4.2 Infinispan Vector Store 配置
yaml
# application.yml
spring:
ai:
infinispan:
vector-store:
enabled: true
config-location: infinispan-config.xml
java
@Configuration
public class InfinispanVectorStoreConfig {
@Bean
public InfinispanVectorStore infinispanVectorStore(
Cache<String, List<Float>> cache,
EmbeddingModel embeddingModel
) {
InfinispanVectorStoreConfig config = InfinispanVectorStoreConfig.builder()
.withCacheName("ai-vectors")
.build();
return new InfinispanVectorStore(cache, embeddingModel, config);
}
@Bean
public Cache<String, List<Float>> vectorCache(
DefaultCacheManager cacheManager
) {
return cacheManager.getCache("ai-vectors");
}
@Bean
public DefaultCacheManager cacheManager() {
return new DefaultCacheManager(
new ConfigurationBuilder()
. clustering()
.cacheMode(CacheMode.REPL_SYNC)
.build()
);
}
}
五、向量存储对比与选型建议
5.1 对比表格
| 特性 | Redis | PostgreSQL PGVector | Milvus | Amazon S3 | Infinispan |
|---|---|---|---|---|---|
| 类型 | 内存缓存 | 关系数据库 | 专用向量 DB | 对象存储 | 分布式缓存 |
| 性能 | 极高 | 高 | 高 | 中 | 高 |
| 扩展性 | 集群 | 分片 | 分布式 | 无限 | 集群 |
| 成本 | 中 | 低 | 中 | 低 | 中 |
| 复杂度 | 低 | 低 | 中 | 低 | 中 |
| 适用场景 | 语义缓存 | 中小规模 RAG | 大规模 RAG | 文档存储 | 分布式缓存 |
5.2 选型建议
选择 Redis 的场景:
- 需要语义缓存
- 要求极高的响应速度
- 数据量适中(百万级以下)
- 已有 Redis 基础设施
选择 PostgreSQL PGVector 的场景:
- 需要关系数据和向量数据共存
- 中小规模 RAG 应用
- 需要事务支持
- 团队熟悉 PostgreSQL
选择 Milvus 的场景:
- 大规模向量数据(亿级以上)
- 需要高性能向量搜索
- 对专用向量数据库有需求
- 有专门的运维团队
选择 Amazon S3 的场景:
- 文档管理是主要需求
- 需要低成本大规模存储
- 已深度使用 AWS 生态
- 需要全球分发
选择 Infinispan 的场景:
- 需要分布式缓存
- 要求高可用和容错
- 已使用 Infinispan
- Java 技术栈
六、实战案例:多向量存储架构
6.1 架构设计
用户请求
↓
┌──────────────┴──────────────┐
↓ ↓
Redis 缓存层 PostgreSQL 主存储
↓ ↓
命中:返回 检索文档
↓ ↓
未命中:↓ └──────┬─────┘
↓ ↓
PostgreSQL 查询 Amazon S3 原始文档
↓ ↓
返回文档 └──────┴─────┘
↓
答案生成
6.2 多层存储实现
java
@Service
public class MultiStoreVectorService {
private final VectorStore redisStore;
private final VectorStore postgresStore;
private final S3Client s3Client;
// 多层检索
public List<Document> searchMultiStore(String query, int topK) {
// 第一层:Redis 缓存
List<Document> redisResults = redisStore.similaritySearch(
SearchRequest.query(query)
.withTopK(topK)
);
if (redisResults.size() >= topK) {
log.info("完全命中 Redis 缓存");
return redisResults.subList(0, topK);
}
// 第二层:PostgreSQL 主存储
List<Document> postgresResults = postgresStore.similaritySearch(
SearchRequest.query(query)
.withTopK(topK)
);
// 合并结果
Set<String> seen = new HashSet<>();
List<Document> combined = new ArrayList<>();
for (Document doc : redisResults) {
String id = doc.getId();
if (seen.add(id)) {
combined.add(doc);
}
}
for (Document doc : postgresResults) {
String id = doc.getId();
if (seen.add(id) && combined.size() < topK) {
combined.add(doc);
// 热点数据回填 Redis
redisStore.add(List.of(doc));
}
}
return combined;
}
// 从 S3 加载完整文档
public String loadFullDocument(Document document) {
String s3Key = (String) document.getMetadata()
.get("s3_key");
if (s3Key != null) {
return loadFromS3(s3Key);
} else {
// 返回存储的内容
return document.getContent();
}
}
private String loadFromS3(String s3Key) {
GetObjectRequest request = GetObjectRequest.builder()
.bucket("ai-documents")
.key(s3Key)
.build();
try (InputStream is = s3Client.getObject(request)) {
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException("加载 S3 文档失败", e);
}
}
}
总结
Spring AI 2.0 的向量存储生态提供了丰富的选择,从高性能的 Redis 到低成本的 Amazon S3,从专用的 Milvus 到关系数据库的 PGVector,几乎涵盖了所有应用场景。
作为一名架构师,我的建议是:
- 根据场景选择:不同场景使用不同的存储后端
- 多层架构:缓存层 + 主存储层 + 冷存储层
- 避免锁定:使用 Spring AI 的统一 API,便于切换
- 监控优化:持续监控性能,优化检索策略
- 成本控制:合理使用生命周期管理,控制成本
向量存储是 RAG 系统的基础,选择合适的存储方案对系统性能和成本都有巨大影响。Spring AI 2.0 为我们提供了灵活的选择,让我们能够根据实际需求构建最优的解决方案。
参考资料:
- Spring AI Vector Store 文档:https://docs.spring.io/spring-ai/reference/api/vectordbs.html
- Redis 官方文档:https://redis.io/
- Amazon Bedrock 文档:https://docs.aws.amazon.com/bedrock/
- Infinispan 文档:https://infinispan.org/