Milvus 向量数据库入门与实战
一、为什么需要向量数据库?
2025 年,大语言模型(LLM)已经深入到软件开发的各个角落。但 LLM 有两个众所周知的短板:
知识截断 —— 模型训练数据有截止日期,无法回答最新问题。
幻觉问题 —— 模型有时会"一本正经地胡说八道"。
RAG(Retrieval-Augmented Generation,检索增强生成) 正是解决这两个问题的核心范式——先检索相关的知识片段,再让 LLM 基于这些片段生成答案。而 RAG 的"检索"环节,靠的就是向量数据库 。
简单来说,向量数据库是一种专门用于存储、索引和检索高维向量嵌入(Embedding)的数据库。它不像 MySQL 那样做精确匹配,而是通过计算向量之间的 相似度 (余弦相似度、欧氏距离等)来找到"语义上最接近"的数据。
而 Milvus,正是这个领域最活跃的开源项目之一。
二、Milvus 是什么?
Milvus 是 Linux 基金会旗下的开源向量数据库,采用 Apache 2.0 协议。它是一个开源的、云原生的向量数据库,专为海量向量数据的存储、索引和相似性搜索而设计。它在 GitHub 上拥有超过 30k Star,已进入 CNCF 毕业项目(Graduated),被广泛应用于语义搜索、推荐系统、图像检索、RAG 等 AI 场景。
截至 2026 年 5 月,Milvus 的稳定版本为 v2.6.x ,v3.0 也已经进入 Beta 阶段(支持多模态数据类型)。
2.1 核心特性
特性
说明
高性能
支持毫秒级十亿级向量检索
10+ 种索引类型
IVF_FLAT、HNSW、DiskANN、SCANN、GPU 加速索引等
多种距离度量
欧氏距离(L2)、内积(IP)、余弦相似度(Cosine)、Jaccard、Hamming
混合查询
向量相似度 + 标量过滤(如 WHERE price > 100 ORDER BY distance)
多语言 SDK
Python、Java 、Go、Node.js、C#、Rust
灵活部署
单机 Docker、Kubernetes 分布式集群、全托管云服务(Zilliz Cloud)
云原生架构
存算分离,支持水平扩展和弹性伸缩
三层存储
v2.6.x 引入引入内存/本地磁盘/对象存储三层分级存储,成本最多可降低 87%
2.2 架构概览
Milvus 采用存算分离 的云原生微服务架构,分为四层:
1 2 3 4 5 6 7 8 9 ┌─────────────────────────────────┐ │ 接入层 (Proxy) │ ← 无状态代理,gRPC/REST ├─────────────────────────────────┤ │ 协调层 (Coordinator) │ ← DDL/DCL、时间戳、任务调度 ├─────────────────────────────────┤ │ 工作节点层 (Worker Nodes) │ ← 流式写入 / 历史查询 / 后台压缩建索引 ├─────────────────────────────────┤ │ 存储层 │ ← etcd(元数据) + S3/MinIO(对象) + Kafka/Woodpecker(WAL) └─────────────────────────────────┘
接入层 :无状态 Proxy,负责请求路由和结果聚合,水平扩展能力极强。
协调层 :管理集群元数据、DDL 操作、时间戳服务。
工作节点 :Streaming Node 处理实时写入,Query Node 处理历史数据查询,Data Node 负责后台压缩与索引构建。
存储层 :etcd 存元数据,S3/MinIO 存持久化数据,Kafka/Woodpecker 做 WAL 保证写入可靠性。
2.3 Milvus vs 其他向量数据库
维度
Milvus
Pinecone
Weaviate
部署方式
自托管 + 云
仅 SaaS
自托管 + 云
索引种类
10+ 种
有限
HNSW、Flat
GPU 加速
✅
❌
❌
开源协议
Apache 2.0
闭源
BSD-3
最大规模
100 亿+
Pod 垂直限制
模块化
三、Milvus 核心概念
在写代码之前,先理解几个关键概念:
概念
类比 MySQL
说明
Database
Database
数据库,逻辑隔离单元
Collection
Table
集合,存储同一类数据的"表"
Field
Column
字段,分为主键字段、向量字段、标量字段
Entity
Row
实体,一行数据
Index
Index
向量索引,加速 ANN 检索
Partition
Partition
分区,提升查询效率
Segment
—
数据段,Milvus 内部的数据管理最小单元
一个典型的 Collection Schema 长这样:
1 2 3 4 ┌──────────────┬──────────────────┬──────────────────────────────────┬────────────────┐ │ id (Int64) │ title (VarChar) │ embedding (FloatVector[768]) │ tags (VarChar) │ │ PRIMARY KEY │ 标量字段 │ 向量字段 │ 标量字段 │ └──────────────┴──────────────────┴──────────────────────────────────┴────────────────┘
四、Milvus Java SDK 基础操作
Milvus 提供了官方的 Java SDK。在 2025 年,推荐使用 milvus-sdk-java 2.6.x 版本,配合 JDK 17+ 和 Spring Boot 3.x。
Milvus Lite 说明 :从 2.4.x 起,SDK 内置了 Milvus Lite,可以在本地以嵌入式方式运行一个轻量 Milvus 实例,无需单独部署服务端,非常适合开发和测试。
4.1 环境准备
Maven 依赖:
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > io.milvus</groupId > <artifactId > milvus-sdk-java</artifactId > <version > 2.6.3</version > </dependency > <dependency > <groupId > com.google.code.gson</groupId > <artifactId > gson</artifactId > <version > 2.11.0</version > </dependency >
启动 Milvus(Docker):
1 2 3 4 5 6 7 8 wget https://github.com/milvus-io/milvus/releases/latest/download/milvus-standalone-docker-compose.yml -O docker-compose.yml docker compose up -d docker ps
4.2 连接 Milvus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import io.milvus.v2.client.ConnectConfig;import io.milvus.v2.client.MilvusClientV2;public class MilvusConnectionDemo { public static void main (String[] args) { ConnectConfig config = ConnectConfig.builder() .uri("http://localhost:19530" ) .token("root:Milvus" ) .build(); try (MilvusClientV2 client = new MilvusClientV2 (config)) { System.out.println("成功连接 Milvus!" ); } } }
4.3 创建 Database 和 Collection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import io.milvus.v2.service.database.request.CreateDatabaseReq;import io.milvus.v2.service.database.request.UseDatabaseReq;import io.milvus.v2.service.collection.request.CreateCollectionReq;import io.milvus.v2.service.collection.request.AddFieldReq;import io.milvus.v2.common.DataType;try { client.createDatabase(CreateDatabaseReq.builder() .databaseName("my_rag_db" ) .build()); } catch (Exception e) { if (!e.getMessage().contains("already exist" )) throw e; } client.useDatabase("my_rag_db" ); CreateCollectionReq.CollectionSchema schema = client.createSchema(); schema.addField(AddFieldReq.builder() .fieldName("id" ) .dataType(DataType.Int64) .isPrimaryKey(true ) .autoID(true ) .build()); schema.addField(AddFieldReq.builder() .fieldName("embedding" ) .dataType(DataType.FloatVector) .dimension(768 ) .build()); schema.addField(AddFieldReq.builder() .fieldName("content" ) .dataType(DataType.VarChar) .maxLength(65535 ) .build()); schema.addField(AddFieldReq.builder() .fieldName("source" ) .dataType(DataType.VarChar) .maxLength(512 ) .build()); client.createCollection(CreateCollectionReq.builder() .collectionName("documents" ) .collectionSchema(schema) .build()); System.out.println("Collection 'documents' 创建成功!" );
4.4 创建向量索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import io.milvus.v2.service.index.request.CreateIndexReq;import io.milvus.v2.common.IndexParam;IndexParam indexParam = IndexParam.builder() .fieldName("embedding" ) .indexType(IndexParam.IndexType.HNSW) .metricType(IndexParam.MetricType.COSINE) .extraParams(Map.of( "M" , 16 , "efConstruction" , 200 )) .build(); client.createIndex(CreateIndexReq.builder() .collectionName("documents" ) .indexParams(List.of(indexParam)) .build()); System.out.println("索引创建成功!" );
常用索引类型对比:
索引类型
适用场景
优点
缺点
FLAT
小数据量(<1 万),需要 100% 召回
最高精度
最慢
IVF_FLAT
中等数据量,平衡精度与速度
速度较快
有一定精度损失
HNSW
生产环境首选
高召回 + 高速度
内存占用较大
DiskANN
超大规模(10 亿+)
磁盘友好
索引构建慢
4.5 插入向量数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import io.milvus.v2.service.vector.request.InsertReq;import com.google.gson.Gson;import com.google.gson.JsonObject;Gson gson = new Gson ();List<JsonObject> rows = new ArrayList <>(); rows.add(buildRow(List.of(0.12f , 0.34f , ), "Milvus 是一个开源向量数据库。" , "doc1.txt" )); rows.add(buildRow(List.of(0.56f , 0.78f , ), "RAG 结合了检索和生成两种能力。" , "doc2.txt" )); client.insert(InsertReq.builder() .collectionName("documents" ) .data(rows) .build()); System.out.println("数据插入成功!" ); private static JsonObject buildRow (List<Float> embedding, String content, String source) { JsonObject obj = new JsonObject (); obj.add("embedding" , gson.toJsonTree(embedding)); obj.addProperty("content" , content); obj.addProperty("source" , source); return obj; }
4.6 向量相似性检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import io.milvus.v2.service.vector.request.SearchReq;import io.milvus.v2.service.vector.request.data.FloatVec;import io.milvus.v2.service.vector.response.SearchResp;import io.milvus.v2.service.collection.request.LoadCollectionReq;import io.milvus.v2.service.collection.request.ReleaseCollectionReq;client.loadCollection(LoadCollectionReq.builder() .collectionName("documents" ) .build()); List<Float> queryVector = embedUserQuestion("什么是向量数据库?" ); SearchResp resp = client.search(SearchReq.builder() .collectionName("documents" ) .annsField("embedding" ) .metricType(IndexParam.MetricType.COSINE) .data(List.of(new FloatVec (queryVector))) .limit(5 ) .searchParams(Map.of("ef" , 64 )) .outputFields(List.of("content" , "source" )) .build()); for (List<SearchResp.SearchResult> resultList : resp.getSearchResults()) { for (SearchResp.SearchResult result : resultList) { System.out.printf("ID: %s | 相似度: %.4f | 内容: %s%n" , result.getId(), result.getScore(), result.getEntity().get("content" )); } } client.releaseCollection(ReleaseCollectionReq.builder() .collectionName("documents" ) .build());
完整的生命周期流程:
1 创建 Collection → 创建 Index → 插入数据 → Load → Search → Release
五、RAG 项目实战:基于本地文档的智能问答
下面我们来构建一个完整的 RAG 问答项目。整体架构如下:
1 2 3 用户提问 → 向量检索(Milvus)→ 召回相关文档片段 → 拼装 Prompt → LLM 生成回答 ↑ 文档加载 → 文本切分 → 向量嵌入(DashScope)
5.1 技术栈
组件
版本
用途
JDK
17+
Java 运行环境
Spring Boot
3.5.9
应用框架
Spring AI
1.1.4
AI 抽象层,提供 ChatClient、VectorStore 等核心 API
Spring AI Alibaba
1.1.2.2
阿里云百炼集成,提供 DashScope 对话模型与嵌入模型
Milvus
2.6.10
向量数据库,存储与检索文档向量
Apache Tika
(由 Spring AI 托管)
文档解析器,支持 PDF、Word、Markdown、TXT 等格式
DashScope (百炼)
text-embedding-v4 / qwen-plus
文本向量嵌入 + 大模型对话
5.2 项目结构
1 2 3 4 5 6 7 8 9 10 11 12 milvus-rag-demo/ ├── pom.xml # Maven 依赖配置 ├── src/main/ │ ├── java/com/example/rag/ │ │ ├── RagApplication.java # Spring Boot 主启动类 │ │ ├── KnowledgeLoader.java # 文档加载 → 切分 → 入库服务 │ │ ├── RagController.java # RAG 问答 REST 接口 │ │ └── AdvancedRagConfig.java # 进阶 RAG 配置(查询改写 + 多路召回) │ └── resources/ │ ├── application.yml # Spring AI + Milvus 配置 │ └── knowledge/ # 待入库的知识文档目录 │ └── 公司考勤制度.md # 示例知识文档
5.3 项目初始化
Maven pom.xml 核心依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 3.5.9</version > </parent > <groupId > com.example</groupId > <artifactId > milvus-rag-demo</artifactId > <version > 1.0.0</version > <properties > <java.version > 17</java.version > <spring-ai.version > 1.1.4</spring-ai.version > <spring-ai-alibaba.version > 1.1.2.2</spring-ai-alibaba.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > com.alibaba.cloud.ai</groupId > <artifactId > spring-ai-alibaba-starter-dashscope</artifactId > </dependency > <dependency > <groupId > org.springframework.ai</groupId > <artifactId > spring-ai-starter-vector-store-milvus</artifactId > </dependency > <dependency > <groupId > org.springframework.ai</groupId > <artifactId > spring-ai-tika-document-reader</artifactId > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.ai</groupId > <artifactId > spring-ai-bom</artifactId > <version > ${spring-ai.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > com.alibaba.cloud.ai</groupId > <artifactId > spring-ai-alibaba-bom</artifactId > <version > ${spring-ai-alibaba.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > </project >
application.yml 配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: model: qwen-plus options: temperature: 0.3 embedding: model: text-embedding-v4 options: dimensions: 1024 vectorstore: milvus: host: localhost port: 19530 database-name: default collection-name: knowledge_base embedding-dimension: 1024 index-type: IVF_FLAT metric-type: COSINE initialize-schema: true
5.4 知识入库服务
下面的 KnowledgeLoader 负责将本地文档加载、切分、向量化后存入 Milvus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com.example.rag;import jakarta.annotation.PostConstruct;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.ai.document.Document;import org.springframework.ai.reader.tika.TikaDocumentReader;import org.springframework.ai.transformer.splitter.TokenTextSplitter;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.beans.factory.annotation.Value;import org.springframework.core.io.Resource;import org.springframework.stereotype.Service;import java.util.List;@Service public class KnowledgeLoader { private static final Logger log = LoggerFactory.getLogger(KnowledgeLoader.class); private final VectorStore vectorStore; @Value("classpath:/knowledge/") private Resource[] knowledgeFiles; public KnowledgeLoader (VectorStore vectorStore) { this .vectorStore = vectorStore; } @PostConstruct public void loadDocuments () { for (Resource file : knowledgeFiles) { try { log.info("正在处理文档: {}" , file.getFilename()); TikaDocumentReader reader = new TikaDocumentReader (file); List<Document> documents = reader.get(); TokenTextSplitter splitter = new TokenTextSplitter (800 , 100 , 5 , 2000 , true ); List<Document> chunks = splitter.apply(documents); vectorStore.add(chunks); log.info("文档 {} 入库完成,切分为 {} 个片段" , file.getFilename(), chunks.size()); } catch (Exception e) { log.error("处理文档失败: {}" , file.getFilename(), e); } } log.info("所有知识文档入库完毕" ); } }
5.5 RAG 问答接口
对外暴露 REST 接口,接收用户问题,执行检索 + 生成的完整流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.example.rag;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestController public class RagController { private static final Logger log = LoggerFactory.getLogger(RagController.class); private final ChatClient chatClient; public RagController (ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { this .chatClient = chatClientBuilder .defaultAdvisors(new QuestionAnswerAdvisor (vectorStore)) .build(); } @GetMapping("/ask") public Answer ask (@RequestParam String question) { log.info("收到问题: {}" , question); String answer = chatClient.prompt() .user(question) .call() .content(); return new Answer (question, answer); } public record Answer (String question, String content) {} }
核心在于 QuestionAnswerAdvisor,它会在每次请求时自动执行以下步骤:
将用户问题向量化
在 Milvus 中检索最相似的 Top-K 文档片段
将检索结果注入 System Prompt
交给大模型生成答案
5.6 主启动类
1 2 3 4 5 6 7 8 9 10 11 12 package com.example.rag;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class RagApplication { public static void main (String[] args) { SpringApplication.run(RagApplication.class, args); } }
5.7 准备测试文档
在 src/main/resources/knowledge/ 目录下放入任意文档,例如下面这个简单的 Markdown 文件:
1 2 3 4 5 6 7 8 9 10 11 12 # 公司考勤制度 ## 年假政策 - 入职满 1 年的员工享有 5 天年假- 入职满 3 年的员工享有 10 天年假- 入职满 5 年的员工享有 15 天年假- 年假需提前 3 个工作日申请## 加班政策 - 工作日加班:按基本工资的 1.5 倍计算- 休息日加班:按基本工资的 2 倍计算- 法定节假日加班:按基本工资的 3 倍计算
5.8 运行与测试
1 2 3 4 5 6 7 8 docker compose up -d export DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxmvn spring-boot:run
启动后,应用会自动读取 knowledge/ 目录下的文档并写入 Milvus。然后通过浏览器或 curl 测试:
1 curl "http://localhost:8080/ask?question=入职两年的员工有多少天年假?"
返回示例:
1 2 3 4 { "question" : "入职两年的员工有多少天年假?" , "content" : "根据公司考勤制度,入职满1年的员工享有5天年假。您入职两年,符合这个条件,因此享有5天年假。" }
5.9 进阶玩法
上面使用的是 QuestionAnswerAdvisor 一行搞定的简化模式。如果业务需要更精细的控制,可以通过 RetrievalAugmentationAdvisor 实现查询改写 和多路召回 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Configuration public class AdvancedRagConfig { @Bean public ChatClient advancedChatClient ( ChatClient.Builder builder, VectorStore vectorStore, ChatModel chatModel) { RewriteQueryTransformer queryTransformer = RewriteQueryTransformer.builder() .chatModel(chatModel) .targetSearchSystem("Milvus 向量数据库中的公司制度文档" ) .build(); MultiQueryExpander queryExpander = MultiQueryExpander.builder() .chatModel(chatModel) .numberOfQueries(3 ) .build(); RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder() .queryTransformers(List.of(queryTransformer, queryExpander)) .documentRetrievers(List.of(VectorStoreDocumentRetriever.builder() .topK(5 ) .similarityThreshold(0.7 ) .vectorStore(vectorStore) .build())) .build(); return builder.defaultAdvisors(advisor).build(); } }
这样 RAG 的召回质量会得到显著提升,对于生产环境中的复杂场景尤为必要。
六、总结
本文从零介绍了 Milvus 向量数据库的核心概念和 Java SDK 基础操作,并给出了一个完整的 Spring Boot RAG 项目。关键要点:
Milvus 是 AI 基础设施的关键一环 :存算分离的云原生架构使其能够支撑 100 亿级向量的实时检索,而丰富的索引类型(HNSW、DiskANN 等)让它在各种场景下都能找到合适的性能方案。
Java 生态已充分支持 :通过 milvus-sdk-java 可以直接操作 Milvus,配合 Spring AI 的 MilvusVectorStore 抽象,开发者几乎不用关心底层细节。
RAG 的核心链路 :文档解析 → 文本分块 → 向量化 → 存入 Milvus → 用户提问向量化 → 相似检索 → 拼接上下文 → LLM 生成答案。
向量数据库的普及让"语义搜索"不再是遥不可及的概念。现在,用 Java 和 Milvus,你也能为你的应用插上 AI 的翅膀。
参考资源: