Milvus 入门:一个向量数据库,管好你的 AI 记忆
导读: 你的 AI 应用上线了,用户量上来了,FAISS 开始喘不上气了……
本文带你认识 Milvus——一个生产级向量数据库,让 AI 的"记忆"也能扛住大流量。
零基础友好 | 附完整代码 | 含动图讲解
一、从一次"翻车"说起
想象你开发了一个 AI 知识助手,用 FAISS 存了几万条向量,跑得飞快,自我感觉良好。
然后有一天,Boss 说:“我们要上线,预计日活几十万。”
你微笑着打开代码……然后发现:
- FAISS 是单机的,不能水平扩展 😶
- 重启服务要重新加载全部向量到内存,半分钟起步 😶
- 没有权限管理,没有数据持久化,没有监控 😶
这时候你需要的不是"向量存储工具",而是一个向量数据库。
而 Milvus,就是这个领域的扛把子。

二、Milvus 是什么?三句话说清楚
💡 一句话定义:Milvus 是一个开源的、专为海量向量相似性搜索设计的云原生数据库,天生为生产环境而生。
你可以把它理解成这样:
- FAISS = 你书桌上的便利贴堆 → 找东西快,但一多就乱,也没法共享
- Milvus = 一座现代化图书馆 → 有馆员(调度器)、有书架标签(索引)、有借阅记录(持久化),还支持多人同时进馆(分布式)
从诞生背景看,Milvus 是由 Zilliz 公司开源,现在是 LF AI & Data 基金会的顶级项目,背书够硬,社区也够活跃。
三、装起来!5 分钟跑通 Milvus
不折腾理论了,先把它跑起来。Milvus 官方推荐用 Docker 部署单机版,步骤超简单。
第一步:下载配置文件
# macOS / Linux
wget https://github.com/milvus-io/milvus/releases/download/v2.5.14/milvus-standalone-docker-compose.yml -O docker-compose.yml
# Windows PowerShell
Invoke-WebRequest -Uri "https://github.com/milvus-io/milvus/releases/download/v2.5.14/milvus-standalone-docker-compose.yml" -OutFile "docker-compose.yml"
第二步:一键启动
docker compose up -d
Docker 会帮你拉起三个容器:
| 容器名 | 职责 |
|---|---|
milvus-standalone |
核心服务,处理所有请求 |
milvus-etcd |
元数据仓库(记住你有哪些集合、字段等) |
milvus-minio |
对象存储(持久化向量文件) |
等容器都变成 Up 状态,Milvus 就在 19530 端口等你了。🎉
停止服务:
docker compose down(保留数据)
彻底清除:docker compose down -v(数据也删)
四、核心概念:Milvus 的"图书馆哲学"
这是本文最重要的一节。Milvus 的设计哲学一点都不神秘,用"图书馆"类比,5 分钟全懂。

动图:Collection → Partition → Entity 的层级关系逐层展开

动图:Milvus 数据层级结构——从图书馆到一本书的逐层拆解(慢速,8秒)
4.1 Collection(集合)≈ 一座图书馆
Collection(集合) 是 Milvus 里最顶层的数据容器,相当于关系型数据库里的"一张表"。
每个 Collection 有一个 Schema(模式),就像图书馆的"入馆规则"——来的数据必须符合规定的字段格式。
Schema 通常包含三类字段:
- 🔑 主键字段:每条数据的唯一 ID(相当于书的 ISBN 号)
- 🧮 向量字段:核心!存放 Embedding 向量(相当于书的"语义坐标")
- 📝 标量字段:附加元数据,如书名、作者、价格(用于过滤检索)
4.2 Partition(分区)≈ 图书馆的分区
一个 Collection 可以划分为多个 Partition(分区),就像图书馆分了"科技区"、“小说区”。
为什么分区? 查询时只搜特定分区,扫描的数据量大幅减少,速度自然快。
最多支持 1024 个分区,合理使用是性能优化的重要手段。
4.3 Alias(别名)≈ 动态的推荐书单
Alias(别名) 是给 Collection 取的"外号"。这个功能听起来鸡肋,但在生产中超级实用:
场景:你需要更新 Collection 里的全量数据(比如重新索引),怎么做到"不停服更新"?
答案就是别名:
- 建一个新的
collection_v2,导入并索引好新数据 - 把指向旧集合的别名
my_app切换到collection_v2 - 用户完全无感,零停机 ✅
五、索引:检索速度的秘密武器
光有数据还不够,向量检索之所以快,靠的是索引(Index)。
索引就像图书馆的"检索目录"——不是挨本翻书,而是先查目录定位区域,再精确找书。

Milvus 提供了 4 种主要向量索引类型:
| 索引类型 | 核心原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| FLAT | 暴力搜索,全量对比 | 100% 召回率 | 速度慢,内存大 | 数据量小、要求精准 |
| IVF 系列 | 先聚类分桶,再桶内搜 | 速度快,吞吐高 | 召回率 <100% | 通用,大规模高吞吐 |
| HNSW | 多层邻近图,层层定位 | 速度极快,召回率高 | 内存占用大 | 实时推荐、低延迟 |
| DiskANN | 图索引 + SSD 优化 | 支持超大规模数据 | 延迟略高于内存 | 数十亿级向量 |
怎么选? 记住这个口诀:
- 追求准确率,数据不大 →
FLAT - 追求性能平衡,通用场景 →
IVF_FLAT/IVF_SQ8 - 追求极速低延迟,内存够用 → ✅
HNSW(大多数场景首选) - 数据超出内存上限 →
DiskANN
动图:4 种索引查找方式对比——FLAT 逐一扫描 vs HNSW 图中飞速跳跃(慢速,10秒)

六、检索进阶:不止找"最相似的"
Milvus 的检索能力远不止"找 Top-K 相似向量",还有几个杀手级功能:
6.1 过滤检索:语义 + 条件,双管齐下
场景:找"和这双鞋最像的商品,但价格要低于300元,且有库存"
这就是过滤检索(Filtered Search)——先用标量字段筛出候选集,再在候选集里做向量检索。
比单纯向量检索精准了不止一个档次。
6.2 范围检索:设定相似度阈值
场景:人脸识别系统,找所有"相似度 > 0.9"的人脸
范围检索(Range Search) 不关心排名,只关心"距离是否在区间内"。在安全验证、异常检测场景非常好用。
6.3 混合检索:多向量一起上
这是 Milvus 最强的功能之一。
现代 RAG 应用常见模式:同时用密集向量(捕捉语义) + 稀疏向量(精确关键词),两路并发检索,结果用 RRF(互惠排名融合) 算法合并——效果比任何一路单独检索都好。
多模态场景同理:用户输入文字 + 图片,系统同时检索文本向量和图像向量,最终融合排序。
七、代码实战:从零构建多模态图片检索
说了这么多理论,来跑代码。本节演示一个完整的图文多模态检索引擎——给定一张图片 + 一段文字,从图库中找出最匹配的图片。
用到的模型:Visualized-BGE(能同时理解图片和文字,输出统一的向量)
动图:多模态检索完整流程——图片+文字输入 → 编码为向量 → Milvus 检索 → 相似图片返回(慢速,12秒)
步骤一:初始化工具
# === 步骤1:导入库,定义常量 ===
import os
from tqdm import tqdm # 进度条,让你知道跑到哪了
from glob import glob # 批量匹配文件路径
import torch
from visual_bge.visual_bge.modeling import Visualized_BGE # 多模态编码模型
from pymilvus import MilvusClient, FieldSchema, CollectionSchema, DataType
import numpy as np
import cv2
from PIL import Image
# -------------------------------------------------------
# 🔧 【按你自己的环境修改这里】
# -------------------------------------------------------
# 模型名称:Hugging Face 上的基础语言模型 ID,不用改
# 它是 BGE 的文本理解部分,Visualized_BGE 内部会用到它
MODEL_NAME = "BAAI/bge-base-en-v1.5"
# 模型权重文件路径:Visualized_BGE 的视觉部分权重(.pth 文件)
# 📥 下载地址:https://huggingface.co/BAAI/bge-visualized
# 改成你本地实际存放 .pth 文件的路径
MODEL_PATH = "./models/Visualized_base_en_v1.5.pth"
# 图片数据目录:存放待检索图片的文件夹路径
# 目录下需要有一个 dragon/ 子文件夹,里面放若干 .png 图片
# 改成你本地的图片目录
DATA_DIR = "./data/dragon_images"
# Collection 名称:相当于数据库里的"表名",随意起,英文+下划线即可
COLLECTION_NAME = "multimodal_demo"
# Milvus 服务地址:本地 Docker 启动后默认是这个,不用改
# 如果你用云托管 Milvus(Zilliz Cloud),改成对应的 URI
MILVUS_URI = "http://localhost:19530"
# -------------------------------------------------------
# === 步骤2:封装编码器 ===
class Encoder:
"""
把图片/文字变成向量的工具人
原理:Visualized_BGE 是一个多模态模型,
它能把图片和文字都"翻译"成同一个向量空间里的坐标,
这样图片和文字就可以直接比较相似度了。
"""
def __init__(self, model_name: str, model_path: str):
# 加载模型:model_name 是文本理解骨干,model_path 是视觉权重
self.model = Visualized_BGE(model_name_bge=model_name, model_weight=model_path)
self.model.eval() # 切换到推理模式(关闭 dropout 等训练专用层)
def encode_query(self, image_path: str, text: str) -> list[float]:
"""
图文混合编码(用于查询时)
同时输入一张图 + 一段文字,输出一个融合了两者语义的向量
"""
with torch.no_grad(): # 不计算梯度,推理时不需要,可以省内存
query_emb = self.model.encode(image=image_path, text=text)
return query_emb.tolist()[0] # 返回 Python list,方便存入 Milvus
def encode_image(self, image_path: str) -> list[float]:
"""
纯图片编码(用于入库时)
只输入图片,输出该图片的语义向量
"""
with torch.no_grad():
query_emb = self.model.encode(image=image_path)
return query_emb.tolist()[0]
步骤二:创建 Collection
# === 步骤3:连接 Milvus,设计 Schema ===
# 初始化编码器,加载模型(第一次运行会比较慢,耐心等待)
encoder = Encoder(MODEL_NAME, MODEL_PATH)
# 连接本地 Milvus 服务(确保 Docker 容器已启动)
milvus_client = MilvusClient(uri=MILVUS_URI)
# 如果同名集合已存在,先删掉重建(开发调试时方便重跑)
# 生产环境中要小心,别误删真实数据!
if milvus_client.has_collection(COLLECTION_NAME):
milvus_client.drop_collection(COLLECTION_NAME)
print(f"⚠️ 已删除旧集合:{COLLECTION_NAME}")
# 随机取一张图片编码,自动探测向量维度
# BGE base 模型输出 768 维,large 模型输出 1024 维
# 这样写的好处:换模型时维度自动适配,不用手动改
image_list = glob(os.path.join(DATA_DIR, "*.png")) # 获取目录下所有 .png 图片
dim = len(encoder.encode_image(image_list[0]))
print(f"📐 检测到向量维度:{dim}")
# 定义 Schema:告诉 Milvus 每条数据长什么样
# 类比:相当于建一张有三列的表
fields = [
# 主键:自增 ID,唯一标识每一条数据(Milvus 自动生成,无需手动赋值)
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
# 向量字段:核心!存放图片的 Embedding,dim 是向量维度
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=dim),
# 标量字段:存图片的文件路径,方便检索到结果后能找到原图
# max_length=512 表示路径字符串最长 512 个字符
FieldSchema(name="image_path", dtype=DataType.VARCHAR, max_length=512),
]
schema = CollectionSchema(fields, description="多模态图文检索")
# 创建集合(相当于在数据库里建表)
milvus_client.create_collection(collection_name=COLLECTION_NAME, schema=schema)
print(f"✅ Collection '{COLLECTION_NAME}' 创建成功,向量维度:{dim}")
运行结果:
✅ Collection 'multimodal_demo' 创建成功,向量维度:768
步骤三:插入数据 + 创建索引
# === 步骤4:把图片编码后写入 Milvus ===
# 遍历所有图片,逐一编码成向量
# tqdm 会显示进度条,图片多的时候很有用
data_to_insert = []
for image_path in tqdm(image_list, desc="生成图像嵌入"):
vector = encoder.encode_image(image_path) # 图片 → 768 维向量
# 每条数据是一个字典,key 对应 Schema 里的字段名
# 注意:id 字段不需要填,Milvus 会自动生成
data_to_insert.append({"vector": vector, "image_path": image_path})
# 批量写入 Milvus(比逐条插入效率高很多)
result = milvus_client.insert(collection_name=COLLECTION_NAME, data=data_to_insert)
print(f"✅ 插入 {result['insert_count']} 条数据")
# === 步骤5:创建 HNSW 索引,加载到内存 ===
# 准备索引参数
index_params = milvus_client.prepare_index_params()
index_params.add_index(
field_name="vector", # 对哪个字段建索引(向量字段)
index_type="HNSW", # 索引类型:多层图结构,速度快、召回率高,推荐首选
metric_type="COSINE", # 距离度量:余弦相似度(值越接近1表示越相似)
params={
"M": 16, # 每个节点最多连接 16 个邻居,越大越准但越占内存
"efConstruction": 256, # 建索引时的搜索范围,越大索引质量越高但越慢
}
)
# 构建索引(类比:给书架建目录,首次建立需要一点时间)
milvus_client.create_index(collection_name=COLLECTION_NAME, index_params=index_params)
# 加载到内存(Milvus 只有 load 之后才能被检索)
# 类比:图书馆开门前把常用书区"搬到"前台备查
milvus_client.load_collection(collection_name=COLLECTION_NAME)
print("✅ 索引创建完成,Collection 已加载到内存,可以开始检索了")
运行结果:
生成图像嵌入: 100%|████████████████| 5/5 [00:03<00:00, 1.52it/s]
✅ 插入 5 条数据
✅ 索引创建完成,Collection 已加载到内存,可以开始检索了
步骤四:执行多模态检索
# === 步骤6:图文混合查询 ===
# 查询图片路径:放一张你想以图搜图的图片
# 这里用 DATA_DIR 目录下的 query.png 作为查询图
query_image_path = os.path.join(DATA_DIR, "query.png")
# 查询文字:描述你在找什么,和图片一起送进编码器
# 图+文的组合查询,比单独用图或单独用文字效果更好
query_text = "一条龙"
# 把图片+文字一起编码成一个查询向量
# 这个向量同时包含了图片的视觉信息和文字的语义信息
query_vector = encoder.encode_query(image_path=query_image_path, text=query_text)
# 在 Milvus 里搜索最相似的 5 张图
search_results = milvus_client.search(
collection_name=COLLECTION_NAME, # 在哪个集合里搜
data=[query_vector], # 查询向量(列表格式,支持批量查询)
output_fields=["image_path"], # 搜到结果后,额外返回哪些字段(这里要图片路径)
limit=5, # 返回最相似的前 5 个结果(即 Top-K 的 K=5)
search_params={
"metric_type": "COSINE", # 距离度量,要和建索引时保持一致
"params": {"ef": 128} # HNSW 检索时的搜索范围,越大越准但越慢,128 是个好默认值
}
)[0] # search 返回的是二维列表(支持批量),[0] 取第一个查询的结果
print("检索结果:")
for i, hit in enumerate(search_results):
# hit['distance'] 是余弦相似度,范围 0~1,越接近 1 越相似
# hit['entity'] 是我们在 output_fields 里指定要返回的字段
print(f" Top {i+1}: 相似度={hit['distance']:.4f} 路径={hit['entity']['image_path']}")
运行结果:
检索结果:
Top 1: 相似度=0.9411 路径='...dragon/dragon01.png'
Top 2: 相似度=0.5818 路径='...dragon/dragon02.png'
Top 3: 相似度=0.5731 路径='...dragon/dragon05.png'
Top 4: 相似度=0.4894 路径='...dragon/dragon04.png'
Top 5: 相似度=0.4100 路径='...dragon/dragon03.png'
Top 1 的相似度高达 0.9411,正是查询图片本身——说明整个检索流程完全正确!🎉
步骤五:清理资源
# === 步骤7:用完释放内存,删掉 Collection ===
milvus_client.release_collection(collection_name=COLLECTION_NAME)
milvus_client.drop_collection(COLLECTION_NAME)
print("✅ 资源已释放")

八、总结
学完本文,你已经掌握了:
- ✅ Milvus 是什么:生产级向量数据库,云原生、高可用、可扩展
- ✅ 怎么理解它:Collection(图书馆)→ Partition(分区)→ Schema(规则)→ Entity(数据)
- ✅ 索引怎么选:大多数场景选 HNSW,超大数据量选 DiskANN
- ✅ 怎么用代码:从创建集合到插入数据到多模态检索,一套流程跑通
进阶路线:
| 阶段 | 方向 | 要掌握的 |
|---|---|---|
| 入门 | 本地单机 | MilvusClient + HNSW + 基础 ANN 检索 |
| 进阶 | 混合检索 | 密集+稀疏向量 + RRF 重排 |
| 生产 | 分布式部署 | Milvus Distributed + 分区策略 + 监控 |

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)