SpringBoot 整合 Milvus 向量数据库实战
从环境搭建到代码落地,一文搞懂如何在 SpringBoot 项目中集成 Milvus 向量数据库,构建自己的 RAG(检索增强生成)知识库系统。
一、前言
在 AI 大模型时代,向量数据库 已成为构建智能应用的核心基础设施。无论是知识库问答(RAG)、语义搜索、图片检索还是推荐系统,都离不开向量数据库的身影。
本文将基于一个真实的 SpringBoot 项目,手把手带你完成 Milvus 向量数据库 的集成,涵盖:
- Milvus 服务端安装与配置
- BGE-M3 本地 Embedding 模型部署(CPU/GPU/在线三种方案)
- SpringBoot 中 Milvus 客户端的配置与 CRUD 操作
- 一个完整的 RAG 知识库查询流程
二、技术栈概览
| 组件 | 版本/选型 | 说明 |
|---|---|---|
| SpringBoot | 2.0.0.RELEASE | 项目基础框架 |
| JDK | 1.8 | Java 运行环境 |
| Milvus | 2.6.1 | 向量数据库(服务端) |
| milvus-sdk-java | 2.6.1 | Java 客户端 SDK |
| BGE-M3 | ONNX 格式 | 本地 Embedding 模型 |
| ONNX Runtime | 1.23.2 | 模型推理引擎 |
| LangChain4j | 0.31.0 | 文档分割工具 |
| Python | 3.10.10 | 用于 pymilvus 调试管理 |
三、环境准备
3.1 Milvus 服务端安装(Linux)
Milvus 支持 Docker 部署和 RPM 包安装两种方式。生产环境推荐使用 RPM 包方式,更稳定且便于管理。
下载 RPM 包
从 Milvus GitHub Releases 下载对应版本的 RPM 包:
# 以 milvus 2.6.9 为例(推荐使用与 SDK 匹配的 2.6.x 版本)
wget https://github.com/milvus-io/milvus/releases/download/v2.6.1/milvus_2.6.9-1_amd64.rpm
安装与启动
# 安装 RPM 包
yum install -y ./milvus_2.6.9-1_amd64.rpm# 验证安装
rpm -qa | grep milvus# 启动 Milvus 服务
systemctl start milvus# 查看服务状态
systemctl status milvus# 设置开机自启
systemctl enable milvus
默认启动后,Milvus 监听 19530 端口,支持 gRPC 和 HTTP 两种协议。
3.2 管理工具 Attu
Attu 是 Zilliz 官方出品的 Milvus 可视化管理工具,强烈推荐安装:
- 支持 Collection 的创建、查看、删除
- 可视化数据浏览与查询
- 索引管理
直接下载对应平台的客户端即可使用,连接时填写 Milvus 服务地址和端口(默认 host:19530)。
3.3 Python 客户端(可选)
用于日常调试和数据管理:
# Python 版本要求 3.10.10
pip install pymilvus==2.4.6
pip install milvus
3.4 本地 Embedding 模型 BGE-M3
向量化是向量数据库的核心前置步骤。本项目使用 BGE-M3 模型,支持本地 CPU 推理和 GPU 推理两种模式。
模型下载
从 bge-m3-onnx 下载 ONNX 格式的模型文件,包含两个核心文件:
bge_m3_tokenizer.onnx— 分词器模型bge_m3_model.onnx— 主模型(输出 1024 维向量)
将模型文件放置到本地目录(如 I:/bgem3/onnx),后续 SpringBoot 会通过 ONNX Runtime 加载。
3.5 Ollama 部署(备选方案)
如果不想在 Java 端加载 ONNX 模型,也可以通过 Ollama 提供 embedding 服务:
安装 Ollama
从 cnb.cool/hex/ollama 下载 Windows 客户端,或使用 Docker 部署。
配置镜像加速
编辑 %USERPROFILE%\.ollama\config.json:
{"registry": {"mirrors": {"registry.ollama.ai": "https://registry.ollama.ai"}}
}
拉取 BGE-M3 模型
ollama pull bge-m3
ollama list
启动后 Ollama 会在本地 11434 端口提供 API 服务,可作为在线 embedding 的备选方案。
四、项目配置
4.1 Maven 依赖
在 pom.xml 中引入核心依赖:
<!-- Milvus Java SDK -->
<dependency><groupId>io.milvus</groupId><artifactId>milvus-sdk-java</artifactId><version>2.6.1</version>
</dependency><!-- ONNX Runtime(本地 CPU 推理) -->
<dependency><groupId>com.microsoft.onnxruntime</groupId><artifactId>onnxruntime_gpu</artifactId><version>1.23.2</version>
</dependency><!-- ONNX Runtime 扩展(分词器) -->
<dependency><groupId>com.microsoft.onnxruntime</groupId><artifactId>onnxruntime-extensions</artifactId><version>0.13.0</version>
</dependency><!-- LangChain4j(文档分割) -->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>0.31.0</version>
</dependency><!-- FastJSON(JSON 处理) -->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version>
</dependency>
版本说明:Milvus SDK 版本
2.6.1与服务端2.6.x保持一致,使用 V2 版 API(MilvusClientV2),代码风格更简洁,推荐使用。
4.2 配置文件
application.yml 中 Milvus 相关配置:
milvus:# 连接协议:grpc 或 httpproxy: grpc# Milvus 服务 IPhost: 192.168.1.250# Milvus 服务端口port: 19530# 数据库名(默认 default)database: default# Embedding 硬件模式:cpu / gpu / onlinehardware: cpu# ONNX 模型本地路径onnxpath: I:/bgem3/onnx# 在线 Embedding 模型名modelname: bge-m3-yidong
五、核心代码实现
5.1 配置类 — MilvusConfig
将 application.yml 中的 Milvus 配置映射为 Java Bean:
package com.haopan.ai.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@ConfigurationProperties(prefix = "milvus")
public class MilvusConfig {// 连接协议:grpc / httpprivate String proxy;// Milvus 服务 IPprivate String host;// Milvus 端口private Integer port;// 数据库名private String database;// 硬件模式:cpu / gpu / onlineprivate String hardware;// ONNX 模型路径private String onnxpath;// 在线 embedding 模型名称private String modelname;public MilvusConfig() {hardware = "cpu"; // 默认 CPU 模式}// getter / setter 略...
}
5.2 MilvusClient Bean 注册
在 Application.java 启动类中创建 MilvusClientV2 Bean:
@Bean(destroyMethod = "close")
@Lazy
public MilvusClientV2 milvusClient(MilvusConfig milvusConfig) {ConnectConfig connectConfig = ConnectConfig.builder().uri(milvusConfig.getProxy() + "://" + milvusConfig.getHost() + ":" + milvusConfig.getPort()).dbName(milvusConfig.getDatabase()).build();return new MilvusClientV2(connectConfig);
}
关键点说明:
@Lazy延迟初始化,避免启动时连接失败导致项目无法启动destroyMethod = "close"确保应用关闭时释放连接- URI 格式:
grpc://192.168.1.250:19530
5.3 Embedding 服务(多模式设计)
项目设计了 策略模式 解耦 Embedding 的实现,通过 milvus.hardware 配置自动切换:
// 接口定义
public interface EmbeddingService {float[] embed(String text);
}
模式一:CPU 本地推理(默认)
@Service
@ConditionalOnProperty(name = "milvus.hardware", havingValue = "cpu", matchIfMissing = true)
public class CPUEmbeddingService implements EmbeddingService {@Autowiredprivate MilvusConfig milvusConfig;@Overridepublic float[] embed(String text) {try {M3Embedder embedder = M3Embedder.getInstance(milvusConfig.getOnnxpath());M3EmbeddingOutput result = embedder.generateEmbeddings(text);return result.getDenseEmbedding(); // 返回 1024 维向量} catch (Exception e) {throw new RuntimeException("Local embedding failed", e);}}
}
模式二:GPU 本地推理
@Service
@ConditionalOnProperty(name = "milvus.hardware", havingValue = "gpu")
public class GPUEmbeddingService implements EmbeddingService {// 与 CPU 模式相同,底层 ONNX Runtime 自动使用 CUDA 加速// 需配置 CUDA 环境变量和 onnxruntime_gpu 依赖
}
模式三:在线 API 调用
@Service
@ConditionalOnProperty(name = "milvus.hardware", havingValue = "online")
public class OnlineEmbeddingService implements EmbeddingService {@Autowiredprivate AiProperties aiProperties;@Autowiredprivate MilvusConfig milvusConfig;@Overridepublic float[] embed(String text) {// 从配置中获取模型信息(支持 OpenAI 兼容 API)ModelConfig config = aiProperties.getConfig(milvusConfig.getModelname());String requestUrl = config.getUrl() + "/embeddings";// 构建请求体Map<String, Object> requestBody = new HashMap<>();requestBody.put("model", config.getModel());requestBody.put("input", text);// 通过 WebClient 调用 /embeddings 接口String result = webClient.post().uri(requestUrl).headers(httpHeaders -> httpHeaders.addAll(headers)).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(requestBody)).retrieve().bodyToMono(String.class).block();// 解析返回的向量JSONObject obj = JSONObject.parseObject(result);JSONArray embeddingArray = obj.getJSONArray("data").getJSONObject(0).getJSONArray("embedding");float[] queryVector = new float[embeddingArray.size()];for (int i = 0; i < embeddingArray.size(); i++) {queryVector[i] = embeddingArray.getFloat(i);}return queryVector;}
}
三种模式对比:
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| CPU 本地 | 无网络依赖,数据安全 | 推理速度较慢 | 小数据量、内网环境 |
| GPU 本地 | 推理速度极快 | 需 GPU 硬件、CUDA 环境 | 大批量数据处理 |
| 在线 API | 免部署模型,跨平台 | 依赖网络,有费用 | 快速验证、云端部署 |
5.4 核心服务 — VectorService
VectorService 封装了 Milvus 的全部 CRUD 操作,是整个系统的核心。
5.4.1 向量查询(语义搜索)
public List<String> query(String queryText, List<String> collectNameList, int limit) {List<String> queryResult = new ArrayList<>();// 1. 将查询文本转为向量float[] queryVector = getEmbedding(queryText);FloatVec floatVector = new FloatVec(queryVector);// 2. 设置搜索参数(内积相似度)JSONObject searchParams = new JSONObject();searchParams.put("metric_type", "IP"); // Inner Product// 3. 遍历多个 Collection 进行搜索for (String collectionName : collectNameList) {SearchReq searchReq = SearchReq.builder().collectionName(collectionName).data(Collections.singletonList(floatVector)).annsField("embeddings") // 向量字段.outputFields(Collections.singletonList("text")) // 返回字段.searchParams(searchParams).topK(limit) // 返回 Top-K.consistencyLevel(ConsistencyLevel.STRONG).build();SearchResp searchResp = milvusClient.search(searchReq);// 4. 提取搜索结果if (searchResp.getSearchResults() != null && !searchResp.getSearchResults().isEmpty()) {List<SearchResp.SearchResult> searchResultList = searchResp.getSearchResults().get(0);for (SearchResp.SearchResult searchItem : searchResultList) {String text = String.valueOf(searchItem.getEntity().get("text"));queryResult.add(text);}}}return queryResult;
}
搜索流程:
用户查询文本 → Embedding 向量化 → Milvus 相似度搜索 → 返回 Top-K 文本
5.4.2 文档分块
使用 LangChain4j 的 DocumentByCharacterSplitter 进行智能分块:
public List<String> splitText(String content) {// 每块 800 字符,块间重叠 20 字符(保留上下文连贯性)DocumentByCharacterSplitter charSplitter = new DocumentByCharacterSplitter(800, 20);Document document = Document.from(content);List<TextSegment> segments = charSplitter.split(document);return segments.stream().map(TextSegment::text).collect(Collectors.toList());
}
5.4.3 数据插入
public void insert(List<String> contentList, String source, String user_id, String collection_name) {List<JsonObject> dataList = new ArrayList<>();for (int i = 0; i < contentList.size(); i++) {String content = contentList.get(i);float[] insertVector = getEmbedding(content);JsonObject dataItem = new JsonObject();dataItem.addProperty("text", content);dataItem.addProperty("source", source);dataItem.addProperty("uid", user_id);dataItem.addProperty("page", i);// 将 float[] 转为 JSON 数组Gson gson = new Gson();JsonArray jsonArray = gson.toJsonTree(insertVector, new TypeToken<float[]>(){}.getType()).getAsJsonArray();dataItem.add("embeddings", jsonArray);dataList.add(dataItem);}// 自动创建 Collection(如果不存在)if (!checkCollectionExist(collection_name)) {addCollection(collection_name);}// 执行插入InsertReq insertReq = InsertReq.builder().data(dataList).collectionName(collection_name).build();milvusClient.insert(insertReq);
}
5.4.4 Collection 创建
public void addCollection(String collection_name) {if (!checkCollectionExist(collection_name)) {// 定义字段结构List<CreateCollectionReq.FieldSchema> fieldSchemaList = new ArrayList<>();// 主键(自增)fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder().name("id").dataType(DataType.Int64).isPrimaryKey(true).autoID(true).build());// 用户 IDfieldSchemaList.add(CreateCollectionReq.FieldSchema.builder().name("uid").dataType(DataType.VarChar).maxLength(500).build());// 文本内容fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder().name("text").dataType(DataType.VarChar).maxLength(50000).build());// 页码fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder().name("page").dataType(DataType.Int16).build());// 向量字段(1024 维)fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder().name("embeddings").dataType(DataType.FloatVector).dimension(1024) // BGE-M3 输出 1024 维.build());// 文档来源fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder().name("source").dataType(DataType.VarChar).maxLength(5000).build());// 构建 SchemaCreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder().fieldSchemaList(fieldSchemaList).build();// 索引配置(IVF_FLAT + 内积相似度)HashMap indexExtraParam = new HashMap();indexExtraParam.put("nlist", 16384);IndexParam indexParam = IndexParam.builder().fieldName("embeddings").metricType(IndexParam.MetricType.IP).indexType(IndexParam.IndexType.IVF_FLAT).extraParams(indexExtraParam).build();// 创建 CollectionCreateCollectionReq createCollectionReq = CreateCollectionReq.builder().collectionName(collection_name).description("database").autoID(true).consistencyLevel(ConsistencyLevel.BOUNDED).collectionSchema(collectionSchema).primaryFieldName("id").vectorFieldName("embeddings").indexParam(indexParam).build();milvusClient.createCollection(createCollectionReq);}
}
Collection 字段设计:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | Int64 | 主键,自增 |
| uid | VarChar(500) | 用户标识 |
| text | VarChar(50000) | 文档分块后的文本 |
| page | Int16 | 分块页码 |
| embeddings | FloatVector(1024) | BGE-M3 向量 |
| source | VarChar(5000) | 文档来源标识 |
5.4.5 数据删除
// 按 source 删除指定 Collection 中的数据
public void delete(String source, String user_id, String collection_name) {if (checkCollectionExist(collection_name)) {DeleteReq deleteReq = DeleteReq.builder().collectionName(collection_name).filter("source=='" + source + "'").build();milvusClient.delete(deleteReq);}
}// 删除整个 Collection
public void deleteCollection(String collection_name) {if (checkCollectionExist(collection_name)) {DropCollectionReq deleteReq = DropCollectionReq.builder().collectionName(collection_name).build();milvusClient.dropCollection(deleteReq);}
}
5.5 REST API 层 — VectorController
对外暴露 RESTful 接口:
@RestController
public class VectorController {@Autowiredprivate VectorService vectorService;// 文本向量化@PostMapping("/embedding/")public Object embedding(@RequestBody Map<String, Object> params) {String input = ConvertOp.convert2String(params.get("input"));return vectorService.getEmbedding(input);}// 向量相似度搜索@PostMapping("/milvus/query/")public Object query(@RequestBody Map<String, Object> params) {String query = ConvertOp.convert2String(params.get("query"));String collection_name = ConvertOp.convert2String(params.get("collection_name"));int limit = StringUtils.isEmpty(params.get("limit")) ? 5 : ConvertOp.convert2Int(params.get("limit"));List<String> collectionNameList = Arrays.asList(collection_name.split("\\;"));return vectorService.query(query, collectionNameList, limit);}// 文本分块 + 插入@PostMapping("/milvus/split_insert/")public Object split_insert(@RequestBody Map<String, Object> params) {String content = ConvertOp.convert2String(params.get("content"));String source = ConvertOp.convert2String(params.get("source"));String user_id = ConvertOp.convert2String(params.get("user_id"));String collection_name = ConvertOp.convert2String(params.get("collection_name"));// 1. 分块List<String> splitList = vectorService.splitText(content);// 2. 插入vectorService.insert(splitList, source, user_id, collection_name);return success();}// 直接插入(已分块数据)@PostMapping("/milvus/insert/")public Object insert(@RequestBody DocumentModel documentModel) {vectorService.insert(documentModel.getContent().stream().map(DocumentPageModel::getPage_content).collect(Collectors.toList()),documentModel.getSource(),documentModel.getUser_id(),documentModel.getCollection_name());return success();}// 删除数据@PostMapping("/milvus/delete/")public Object delete(@RequestBody Map<String, Object> params) {vectorService.delete(ConvertOp.convert2String(params.get("source")),ConvertOp.convert2String(params.get("user_id")),ConvertOp.convert2String(params.get("collection_name")));return success();}// 删除 Collection@PostMapping("/milvus/delete_collection/")public Object delete_collection(@RequestBody Map<String, Object> params) {vectorService.deleteCollection(ConvertOp.convert2String(params.get("collection_name")));return success();}
}
5.6 RAG 应用集成
项目将 Milvus 向量搜索深度集成到了大模型对话中(OpenAiService),实现完整的 RAG 流程:
// 对话时自动进行向量搜索增强
if (aiProperties.isQueryvector()) {if (!StringUtils.isEmpty(collection_name) && !collection_name.equals("common")) {// 1. 向量搜索List<String> collectionNameList = Arrays.asList(collection_name.split("\\;"));List<String> promptList = vectorService.query(query, collectionNameList, 5);// 2. 组装 Prompt 模板String promptTemplate = "你是一位文献专家,请结合上下文和检索出的内容回答问题:\n" +"### 上下文\n" + context + "\n" +"### 检索内容\n" + String.join(",", promptList) + "\n" +"### 问题\n" + query + "\n" +"## 回答必须实事求是。\n" +"## 若上下文与问题无关请忽略。只根据检索内容回答。\n" +"## 若检索内容与问题无关请忽略,自己回答。";// 3. 发送给大模型// ...}
}
六、完整调用流程
6.1 知识入库流程
┌──────────┐ ┌──────────────┐ ┌───────────┐ ┌──────────┐
│ 原始文档 │ → │ 智能分块(800) │ → │ 向量化生成 │ → │ Milvus │
│ (PDF等) │ │ 重叠(20) │ │ BGE-M3 │ │ Insert │
└──────────┘ └──────────────┘ └───────────┘ └──────────┘
API 调用示例:
curl -X POST http://localhost:9050/milvus/split_insert/ \-H "Content-Type: application/json" \-d '{"content": "Milvus 是由 Zilliz 开发的开源向量数据库...","source": "doc_001","user_id": "user_123","collection_name": "my_knowledge"}'
6.2 语义检索流程
┌──────────┐ ┌──────────────┐ ┌───────────┐ ┌──────────┐
│ 用户提问 │ → │ 向量化 │ → │ Milvus │ → │ 检索结果 │
│ "什么是.."│ │ BGE-M3 │ │ Search │ │ Top-K │
└──────────┘ └──────────────┘ └───────────┘ └──────────┘↓
┌──────────┐ ┌──────────────┐ ┌───────────┐ ┌──────────┐
│ 最终回答 │ ← │ LLM 生成 │ ← │ Prompt │ ← │ 拼接上下文│
└──────────┘ └──────────────┘ └───────────┘ └──────────┘
API 调用示例:
curl -X POST http://localhost:9050/milvus/query/ \-H "Content-Type: application/json" \-d '{"query": "什么是向量数据库?","collection_name": "my_knowledge","limit": 5}'
七、总结
本文完整呈现了 SpringBoot 整合 Milvus 向量数据库的全过程,核心要点回顾:
| 模块 | 关键技术 | 要点 |
|---|---|---|
| 服务端 | Milvus 2.6.1 RPM 部署 | 默认 gRPC 19530 端口 |
| 客户端 | milvus-sdk-java 2.6.1 | 使用 MilvusClientV2 API |
| Embedding | BGE-M3 + ONNX Runtime | 支持 CPU/GPU/在线三种模式 |
| 文档处理 | LangChain4j | 800 字符分块 + 20 字符重叠 |
| 索引策略 | IVF_FLAT + 内积相似度 | nlist=16384 |
| 一致性 | BOUNDED | 兼顾性能与一致性 |
项目亮点:
- 多模式 Embedding:通过
@ConditionalOnProperty实现 CPU/GPU/在线三种方案的自动切换,满足不同部署场景 - 策略模式解耦:
EmbeddingService接口 + 多种实现,清晰分离向量化逻辑与业务代码 - 完整的RAG链路:从文档入库 → 分块 → 向量化 → 相似搜索 → LLM 增强回答,端到端闭环
- 生产级设计:Lazy 连接、destroyMethod 资源释放、自动建 Collection、异常处理
希望本文能帮助你在自己的项目中快速集成 Milvus 向量数据库,构建出强大的智能应用!
参考资料
- Milvus 官方文档
- Milvus Java SDK
- BGE-M3 ONNX 模型
- Attu 管理工具
- LangChain4j
