Milvus快速入門及用Java操作Milvus的全過程
Milvus 核心概念
向量數(shù)據(jù)庫
- 可以把 Milvus 理解成一個專門處理“向量”這種特殊數(shù)據(jù)的數(shù)據(jù)庫。傳統(tǒng)數(shù)據(jù)庫(如 MySQL)擅長處理數(shù)字、文字這類結構化數(shù)據(jù),并按精確條件查詢(比如 WHERE age > 18)。而向量數(shù)據(jù)庫的核心是??相似性搜索??,它通過計算向量之間的“距離”或“相似度”,來找到最相近的結果。
- “向量”在這里,通常是由各種 AI 模型(如 BERT、ResNet)從文本、圖片、音頻等非結構化數(shù)據(jù)中提取出的特征表示。這些向量本身是一串數(shù)字,在高維空間中代表原始數(shù)據(jù)的意義。
核心概念
- 集合 (Collection) ??:這是 Milvus 中最頂層的概念,類似于關系型數(shù)據(jù)庫中的“表”。一個 Collection 用來存儲具有相同結構的實體。
- 字段 (Field)??:定義了 Collection 的結構,類似于表中的“列”。Milvus 支持多種數(shù)據(jù)類型,但對于向量搜索,最重要的就是 ??FloatVector?? 類型的字段。
- 向量維度 (Dimension)??:這是向量的一個關鍵屬性。它定義了向量中數(shù)字的個數(shù)。例如,一個由 BERT-base 模型生成的句子向量可能是 768 維,這就意味著它是一個包含 768 個浮點數(shù)的數(shù)組。在定義向量字段時,必須指定其維度。
- 索引 (Index) ??:為了在億級數(shù)據(jù)中快速搜索,Milvus 不會進行暴力比對,而是為向量數(shù)據(jù)構建索引。不同的索引類型在??構建速度、內存占用、查詢速度和精度??之間有不同的權衡,常見的索引類型有:
| ??索引類型 | 說明 |
|---|---|
| FLAT | 暴力搜索,精度100%但速度慢,適合小數(shù)據(jù)集或驗證結果 |
| ??IVF_FLAT?? | 先通過聚類將數(shù)據(jù)分桶(nlist 參數(shù)決定桶數(shù)),搜索時只查詢最近的幾個桶(nprobe 參數(shù)控制),大大提升速度 |
| ??HNSW?? | 基于圖論的索引,適合高精度、低延遲的搜索場景,但內存消耗較高 |
- ??度量類型(Metric Type)??:定義了計算向量之間距離或相似度的方式。常見的有:
| ??度量類型 | 說明 |
|---|---|
| ??L2?? (歐氏距離) | 距離越小,向量越相似。 |
| ??IP?? (內積) | 內積越大,向量越相似。 |
| ????COSINE?? (余弦相似度) | 忽略向量大小,只比較方向,相似度值越接近 1 越相似。這在自然語言處理中非常常用 |
Milvus 的基本工作流程

上面這個圖是Milvus的核心系統(tǒng)架構圖,和mysql、pg等數(shù)據(jù)庫的系統(tǒng)架構很像,其核心工作流程可以概括為以下幾個步驟:
- 連接 Milvus??:你的應用程序通過 Milvus 提供的 SDK(如 Python 或 Java SDK)連接到 Milvus 服務。
- 創(chuàng)建集合??:定義一個 Collection,并指定它的字段結構(例如,一個主鍵字段 id ,一個向量字段 embedding和對應的原始數(shù)據(jù)字段,一般一個Collection最少要包含這三個字段)。
- 插入數(shù)據(jù)??:將你的數(shù)據(jù)(向量和相關的元數(shù)據(jù),包括原始數(shù)據(jù))插入到 Collection 中。
- 創(chuàng)建索引??:在向量字段上構建索引(例如 IVF_FLAT)。??這一步至關重要,只有在構建索引后,才能進行高效的向量搜索。
- 加載集合??:將 Collection 從存儲層加載到內存中,以便執(zhí)行查詢。
- 向量搜索??:提供一個查詢向量,讓 Milvus 在 Collection 中搜索最相似的向量,并返回結果,值得注意的是,一般情況下返回的是最相似向量對應的原始數(shù)據(jù)或者自定義的元數(shù)據(jù),而不是返回embedding
下面這個流程圖是Milvus 的核心工作流程圖,很清晰地展示了 Milvus 如何通過向量查找內容的工作過程

用 Java 操作 Milvus
可以總結為,在boot項目中將Milvus客戶端bean注入IOC容器,以便后續(xù)通過客戶端對象操作Milvus服務器,包括創(chuàng)建集合、插入數(shù)據(jù)、創(chuàng)建索引、搜索向量等。主要是核心代碼片段,有boot項目經(jīng)驗的很容易掌握。
引入依賴
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.5.11</version> <!-- 請查看GitHub更新最新版本 -->
</dependency>springBoot中注入Milvus客戶端bean
@Configuration
public class MilvusConfig {
@Value("${milvus_uri}")
private String milvus_uri;
@Bean
public MilvusClientV2 milvusServiceClient() {
ConnectConfig connectConfig = ConnectConfig.builder()
.uri(milvus_uri)
.build();
return new MilvusClientV2(connectConfig);
}
}創(chuàng)建集合
- 一般三個字段,主鍵字段,embedding字段,元數(shù)據(jù)(原始數(shù)據(jù))字段
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.common.DataType;
public class MilvusCollectionCreator {
public static void main(String[] args) {
// 1. 配置并創(chuàng)建 Milvus 客戶端
ConnectConfig connectConfig = ConnectConfig.builder()
.uri("http://localhost:19530") // Milvus 服務器地址
.token("username:password") // 若啟用認證,請?zhí)峁环駝t可省略
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
// 2. 定義集合名稱
String collectionName = "my_first_collection"; // 替換為你想要的集合名
// 3. 構建集合 Schema (字段定義)
CreateCollectionReq.CollectionSchema schema = client.createSchema(); // 創(chuàng)建空Schema
// 3.1 添加主鍵字段 (通常是INT64或VarChar)
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true) // 設為主鍵
.autoID(false) // 設置為false表示插入數(shù)據(jù)時需自行提供ID
.build());
// 3.2 添加標量字段 (例如存儲文本名稱)
schema.addField(AddFieldReq.builder()
.fieldName("name")
.dataType(DataType.VarChar)
.maxLength(255) // VarChar類型必須指定最大長度
.build());
// 3.3 添加向量字段 (核心部分)
int vectorDimension = 768; // 必須與你后續(xù)要插入的向量維度一致!
schema.addField(AddFieldReq.builder()
.fieldName("vector_embedding") // 字段名
.dataType(DataType.FloatVector) // 數(shù)據(jù)類型為浮點向量
.dimension(vectorDimension) // 必須指定向量維度
.build());
// 4. 構建創(chuàng)建集合的請求
CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
.collectionName(collectionName)
.collectionSchema(schema) // 設置定義好的Schema
// .shardsNum(1) // (可選) 設置分片數(shù),默認為1
// .enableDynamicField(true) // (可選) 是否啟用動態(tài)字段以存儲未定義的字段
.build();
// 5. 執(zhí)行創(chuàng)建操作
client.createCollection(createCollectionReq);
System.out.println("集合 '" + collectionName + "' 創(chuàng)建成功!");
}
}插入數(shù)據(jù)
插入數(shù)據(jù)包含主鍵字段、向量數(shù)組、和對應的原始文本數(shù)組??,并確保它們的順序一致,這樣每條向量就與它對應的文本通過相同的數(shù)組索引關聯(lián)起來了
/**
* 插入單條數(shù)據(jù)到Milvus集合
* @param id 記錄的唯一ID
* @param text 原始文本或其他標量數(shù)據(jù)
* @param vector 文本對應的向量(Float列表)
* @return 插入結果信息
*/
public String insertSingleData(Long id, String text, List<Float> vector) {
try {
// 1. 構建一個 JsonObject 代表一條記錄
JsonObject entity = new JsonObject();
entity.addProperty("id", id); // 主鍵字段 (需與集合Schema定義匹配)
entity.addProperty("content", text); // 標量字段 (例如存儲原始文本)
// 將向量列表轉換為JsonArray并添加到實體中
entity.add("vector", gson.toJsonTree(vector)); // 向量字段 (字段名需與Schema匹配)
// 2. 將單條數(shù)據(jù)放入列表
List<JsonObject> dataList = Collections.singletonList(entity);
// 3. 構建插入請求
InsertReq insertReq = InsertReq.builder()
.collectionName(COLLECTION_NAME)
.data(dataList)
.build();
// 4. 執(zhí)行插入
InsertResp insertResp = milvusClientV2.insert(insertReq);
return "成功插入 " + insertResp.getInsertCnt() + " 條數(shù)據(jù)。";
} catch (Exception e) {
throw new RuntimeException("插入數(shù)據(jù)失敗: " + e.getMessage(), e);
}
}創(chuàng)建索引
import io.milvus.param.index.CreateIndexParam;
import io.milvus.param.index.IndexType;
import io.milvus.param.index.MetricType;
CreateIndexParam createIndexParam = CreateIndexParam.newBuilder()
.withCollectionName("my_book_collection")
.withFieldName("embedding") // 在哪個向量字段上建索引
.withIndexType(IndexType.IVF_FLAT) // 選擇索引類型
.withMetricType(MetricType.COSINE) // 選擇度量類型
.withExtraParam("{\"nlist\":1024}") // 索引參數(shù),nlist是聚類中心數(shù)
.build();
milvusClient.createIndex(createIndexParam);
System.out.println("Index created successfully!");檢索并獲取原始數(shù)據(jù)
public Map<String, String> query(List<Float> embedding) {
// key:原始文本,value:drug_code
Map<String, String> res = new HashMap<>();
//設置查詢參數(shù)
Map<String, Object> searchParamsMap = new HashMap<>();
searchParamsMap.put("nprobe", 10); // 設置搜索的桶數(shù)量,影響精度和性能
// 設置返回的field
List<String> returnFields = Arrays.asList("original_text", "drug_code");
FloatVec floatVec = new FloatVec(embedding);
SearchReq searchReq = SearchReq.builder()
.collectionName(DRUG_WES_COLLECTION_NAME)
.data(Collections.singletonList(floatVec))
.topK(TOK_K)
.outputFields(returnFields)
.searchParams(searchParamsMap)
.build();
SearchResp searchResp = milvusServiceClient.search(searchReq);
//獲取查詢向量檢索出的數(shù)據(jù)
List<SearchResp.SearchResult> searchResult = searchResp.getSearchResults().get(0);
for (SearchResp.SearchResult sr : searchResult) {
Map<String, Object> entity = sr.getEntity();
String originalText = (String) entity.get("original_text");
String drugCode = (String) entity.get("drug_code");
res.put(originalText,drugCode);
}
return res;
}工程建議
- 索引選擇??:如果你的數(shù)據(jù)量巨大(十億級)且對查詢速度要求極高,可以考慮 HNSW 索引。如果對內存敏感,可以考慮量化索引如 IVF_PQ
- 參數(shù)調優(yōu)??:nprobe(對于 IVF 系列索引)和 ef(對于 HNSW 索引)等參數(shù)對搜索性能和精度有直接影響。通常需要在你的數(shù)據(jù)集上進行實驗,找到平衡點
- 硬件加速??:Milvus 支持 GPU 加速索引構建和查詢,如果你的環(huán)境有 GPU,可以顯著提升性能
- 官方文檔 https://milvus.io/docs
到此這篇關于Milvus快速入門及用Java操作Milvus的全過程的文章就介紹到這了,更多相關java milvus操作內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決HttpServletRequest無法獲取@RequestBody修飾的參數(shù)問題及分析
Spring Boot中,@RequestBody讀取請求體后流關閉,導致Service層無法獲取,解決方案包括創(chuàng)建HTTP工具類、請求體復制包裝器及攔截器配置,確保請求體可重復讀取2025-09-09
Java實現(xiàn)Word文檔變量的添加與修改操作詳解
文檔變量以鍵值對的形式存儲在文檔的元數(shù)據(jù)中,通過在正文中插入域(Field)來引用這些變量,可以實現(xiàn)一處修改全局同步的效果,本文將介紹如何利用 Java 代碼,通過一個第三方的 Word 文檔處理庫,實現(xiàn)文檔變量的添加與修改操作,有需要的小伙伴可以了解下2026-05-05
java中BeanUtils.copyProperties的用法(超詳細)
本文介紹了BeanUtils.copyProperties()方法的使用,包括其功能、用法、注意事項和示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-08-08
Java volatile四種內存屏障的作用與生效機制原理詳解
內存屏障是處理器提供的一種指令,用于控制指令執(zhí)行順序和內存可見性,在Java中,volatile關鍵字就是通過插入內存屏障來實現(xiàn)其內存語義的,下面我將詳細解釋四種內存屏障的含義和工作原理,感興趣的朋友一起看看吧2025-09-09
java.lang.NullPointerException出現(xiàn)的幾種原因及解決方案
這篇文章主要介紹了java.lang.NullPointerException出現(xiàn)的幾種原因及解決方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
java 中用split分割字符串,最后的空格等不被拆分的方法
下面小編就為大家?guī)硪黄猨ava 中用split分割字符串,最后的空格等不被拆分的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02
Java調用Oss JDk實現(xiàn)刪除指定目錄下的所有文件
這篇文章主要為大家詳細介紹了Java如何調用Oss JDk實現(xiàn)刪除指定目錄下的所有文件功能,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2025-03-03

