十、向量数据库

说明:文中所有类名 / 方法名均在本地 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);
相关推荐
Zan10 小时前
SQLite + SQLx 实战:为什么已经发布的数据库迁移文件,绝对不能再修改?
数据库·后端·面试
这个DBA有点耶11 小时前
数据同步的三个工程底线:DDL同步、断点续传、一致性校验——开源CDC的边界与KFS的路径
数据库·dba·代码规范
SelectDB12 小时前
列式存储为什么适合分析类负载?原理、限制与日志场景应用
数据库
数据库小学妹13 小时前
MySQL大字段治理:行溢出机制、覆盖索引与拆表
数据库·mysql·性能优化
爱吃奥利奥_wen13 小时前
SQL Server 基础语法详解:从建库建表到约束与增删改查
服务器·数据库·oracle
这个DBA有点耶14 小时前
MySQL 8.0不可见索引:删索引怕出事,不删又拖慢写入——中间态怎么用?
数据库·mysql·代码规范
测试界的小码农15 小时前
【图数据库实战进阶(01):从零认识 Neo4j,搭建第一个图数据库项目】
数据库·neo4j
x_SpiderMan15 小时前
搭建Oracle RAC 集群
数据库·oracle
测试界的小码农16 小时前
【图数据库实战进阶(03):Neo4j 图数据建模实战——节点、关系与属性应该怎么设计?】
数据库·oracle·neo4j