十、向量数据库

说明:文中所有类名 / 方法名均在本地 spring-ai 1.1.3 的 jar 中核对过。中文文档(1.0 时代示例)里几处旧 API,统一整理在最后的「踩坑清单」。

1. 它是什么

  • 传统关系库:精确匹配 (where id = ?)。

  • 向量库:相似性搜索 ------ 给一个查询向量,返回"相似"的向量(KNN)。

  • 向量库本身不生成向量 ,只负责存储 + 检索。向量由 EmbeddingModel 生成(float[])。

  • 在 RAG 里的位置:

    灌库:文档 → 切分 → EmbeddingModel 向量化 → VectorStore 存储
    提问:问题 → 向量化 → 相似度检索 topK → 检索结果作为上下文拼进提示词 → 大模型回答

2. 核心 API(1.1.3 实测签名)

java 复制代码
// 只读检索(函数式接口,最小权限原则)
@FunctionalInterface
public interface VectorStoreRetriever {
    List<Document> similaritySearch(SearchRequest request);
    default List<Document> similaritySearch(String query) { ... }
}

// 读写
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {
    void add(List<Document> documents);
    void delete(List<String> idList);
    void delete(Filter.Expression filterExpression);
    default void delete(String filterExpression) { ... }   // 字符串 DSL
    default <T> Optional<T> getNativeClient() { ... }      // 拿底层原生客户端
}

Document 常用方法(注意是 getText()):

java 复制代码
doc.getId();
doc.getText();
doc.getMetadata();
doc.getScore();

SearchRequest 默认值:topK = 4、similarityThreshold = 0.0(即 SIMILARITY_THRESHOLD_ACCEPT_ALL,不过滤)。

3. 支持的 19 种实现

Azure / Cassandra / Chroma / Elasticsearch / GemFire / MariaDB / Milvus / MongoDB Atlas / Neo4j / OpenSearch / Oracle / PgVector / Pinecone / Qdrant / Redis / SAP Hana / Typesense / Weaviate,外加 SimpleVectorStore(内存实现,教学用)。

4. 三种接入方式

A. Starter 自动配置(推荐)

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-milvus</artifactId>
</dependency>
yaml 复制代码
spring:
  ai:
    vectorstore:
      milvus:
        client: { host: localhost, port: 19530 }
        initialize-schema: true     # ⚠ 1.1 起默认 false,需要显式打开
        embedding-dimension: 1536   # 必须和 embedding 模型维度一致

这样才会有 VectorStore bean 可以 @Autowired。

B. 手动配置(不用自动配置)

依赖换成非 starter 的 spring-ai-milvus-store,然后:

java 复制代码
@Bean
public VectorStore vectorStore(MilvusServiceClient client, EmbeddingModel embeddingModel) {
    return MilvusVectorStore.builder(client, embeddingModel)
            .collectionName("test_vector_store")
            .indexType(IndexType.IVF_FLAT)
            .metricType(MetricType.COSINE)
            .batchingStrategy(new TokenCountBatchingStrategy())
            .initializeSchema(true)
            .build();
}

C. SimpleVectorStore(本模块在用)

不会被自动配置,必须自己声明:

java 复制代码
@Bean
public SimpleVectorStore vectorStore(EmbeddingModel embeddingModel) {
    return SimpleVectorStore.builder(embeddingModel).build();
}

内存存储,重启即丢,只适合演示 / 教学。

5. 读写删最小示例

java 复制代码
// 写
vectorStore.add(List.of(new Document("Spring AI rocks", Map.of("country", "BG"))));

// 读(两种方式等价)
List<Document> docs  = retriever.similaritySearch("Spring");
List<Document> docs2 = retriever.similaritySearch(SearchRequest.builder()
        .query("Spring").topK(5).similarityThreshold(0.7).build());

// 删:按 ID / 按过滤表达式 / 按字符串表达式
vectorStore.delete(List.of(doc.getId()));
vectorStore.delete(b.eq("country", "BG").build());
vectorStore.delete("country == 'BG'");

性能:按 ID 删最快;按过滤器删可能扫索引;大批量删除要分批做。

6. 元数据过滤(只作用于 metadata,类似 SQL where)

字符串 DSL:

复制代码
"country == 'BG'"
"genre in ['comedy','drama'] && year >= 2020"

编程式:

java 复制代码
FilterExpressionBuilder b = new FilterExpressionBuilder();
Filter.Expression exp = b.and(b.in("genre", "drama", "documentary"),
                              b.not(b.lt("year", 2020))).build();

运算符:

  • 比较:== != > >= < <=
  • 组合:AND/&&、OR/||
  • 其它:IN、NIN、NOT、IS NULL / IS NOT NULL(并非所有向量库都实现了 NULL 判断)

本项目提醒:KnowledgeBase 的元数据是 source / category,笔记示例里的 type == 'Spring'、genre == 'fairytale' 命中不了,要么改语料,要么改过滤表达式。

7. 批处理策略(大批量灌库必看)

  • 接口:org.springframework.ai.embedding.BatchingStrategy#batch(List<Document>)。
  • 默认实现 TokenCountBatchingStrategy:上限 8191 token、预留 10%,即实际 8191 * 0.9;单文档超限直接抛异常。
  • 覆盖默认:注册一个自己的 bean 即可(自动配置会被替换)。
java 复制代码
@Bean
public BatchingStrategy customBatchingStrategy() {
    return new TokenCountBatchingStrategy(EncodingType.CL100K_BASE, 8000, 0.1);
}
  • 若 embedding 模型支持 autoTruncate(如 Vertex AI),要把批处理上限设成模型实际限制的 5~10 倍,否则批策略先抛异常、模型根本没机会截断。
  • 但静默截断会丢掉长文档尾部信息 → 更推荐灌库前先切分。

8. 读写分离写法(推荐)

灌库服务依赖 VectorStore,检索服务只依赖 VectorStoreRetriever:

java 复制代码
@Service
class DocumentRetriever {
    private final VectorStoreRetriever retriever;

    DocumentRetriever(VectorStoreRetriever retriever) { this.retriever = retriever; }

    List<Document> findSimilar(String query) { return retriever.similaritySearch(query); }
}

好处:最小权限、依赖更少、函数式接口可用 lambda / 方法引用造测试替身。

9. ⚠ 1.1.3 踩坑清单(文档 vs 实际)

文档 / 教程写法 1.1.3 实际
Document::getContent Document::getText(另有 getFormattedContent())
chatModel.generate(prompt) chatModel.call(String)
schema 自动初始化 initialize-schema 默认 false,需显式打开
直接 @Autowired VectorStore 必须有具体 starter 或自己声明 bean;SimpleVectorStore 不自动配置
Document.getScore() 一定有值 存在该 API,但不匹配时为 null,用前先判空

本项目专属坑:DeepSeek 没有 embedding 接口 ,spring.ai.openai.embedding.* 必须另配服务(SiliconFlow / 通义兼容模式 / Ollama),否则 add() 直接 404;且入库与检索必须用同一个 embedding 模型,否则维度不匹配。

java 复制代码
List<Document> docs = new JsonReader(new FileSystemResource(file), "name", "description").get();
vectorStore.add(docs);
相关推荐
曹牧2 小时前
C#:空合并运算符
java·前端·数据库
小羊没烦恼!3 小时前
铁道部新客票系统设计(二)
开发语言·前端·数据库·python·oracle·c#·php
泡海椒4 小时前
JQuick-SQL JOIN 查询:连接注册数据集的正确方式
数据库·sql
xiaogai_gai5 小时前
钉钉月结报销到金蝶付款单同步方案实战教程
大数据·数据库·钉钉
Nturmoils5 小时前
报表总在扫大表,列存储应该放在哪一层
数据库
夜雪一千5 小时前
ShardingSphere 雪花 / UUIDv7 主键分片实战 + SQL 建表示例
数据库·sql
Elastic 中国社区官方博客6 小时前
Elasticsearch:安装自行管理的 Elastic Stack 集群
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·全文检索
PingCAP6 小时前
Agent 从 Demo 到生产,到底卡在哪?
数据库·tidb·agent
SelectDB7 小时前
开源替代怎么选:能力对照、三条部署路径与四类边界速查
数据库·云原生·开源
SelectDB7 小时前
自建与托管怎么选:开源自建、私有化与云上托管三条路径速查
数据库·云原生·开源