一、概述
Milvus 是一款开源的向量数据库,专门用于处理海量向量的相似性搜索,是构建生成式 AI(GenAI)应用的核心基础设施。它跟传统的关系型数据库不一样,主要处理的是从非结构化数据(如图片、文本、音视频)转换而来的嵌入向量,能支撑十亿甚至万亿级的向量检索,广泛应用于 RAG、AI Agent、大模型记忆、推荐系统和以图搜图等场景。
二、使用流程
设计架构(Schema)→ 建索引(Index) → 根据架构创建集合(Collection) → 存数据(Insert) → 取数据(Search / Query)
| 概念 | 类比 | 作用 |
|---|---|---|
| Schema(架构) | 房间设计 | 预先定义这个房间里有哪些货架、每个货架放什么类型的东西 |
| Collection(集合) | 仓库里的一个房间、table | 存一类数据,比如"商品名房间"、"文档切片房间" |
| Field(字段) | 房间里的格子/货架 | 定义每种数据放哪,比如"文字放A格"、"向量放B格" |
| Entity(实体/行) | 一个箱子、row | 一条完整的数据,包含所有字段 |
| Index(索引) | 智能导航图 | 让查找变快,不用翻遍所有箱子 |
和传统数据库的区别:MySQL 存的是"表格行",Milvus 存的是"向量+业务字段",核心能力是找相似的向量。
三、使用步骤实例
步骤 1:环境配置
配置通过环境变量管理,封装 milvus_config 统一读取:
@dataclass
class MilvusConfig:
milvus_url: str # Milvus 服务端地址
chunks_collection: str # 切片集合名
entity_name_collection: str # 预留集合
item_name_collection: str # 商品名集合名
milvus_config = MilvusConfig(
milvus_url=os.getenv("MILVUS_URL"),
chunks_collection=os.getenv("CHUNKS_COLLECTION"),
item_name_collection=os.getenv("ITEM_NAME_COLLECTION")
)
步骤 2:客户端连接(单例模式)
实现 全局单例客户端,避免重复创建连接:
from pymilvus import MilvusClient
_milvus_client = None
def get_milvus_client():
global _milvus_client
if _milvus_client is None:
_milvus_client = MilvusClient(uri=milvus_config.milvus_url)
return _milvus_client
设计要点:单例模式保证全局只有一个连接实例,减少资源消耗,提升性能。
步骤 3:集合(Collection)初始化
集合不存在时 自动创建,包含 Schema 定义 + 索引构建。以商品名集合为例:
# 1. 创建 Schema:自增主键 + 支持动态字段
schema = client.create_schema(auto_id=True, enable_dynamic_field=True)
# 2. 添加字段
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(field_name="file_title", datatype=DataType.VARCHAR, max_length=65535)
schema.add_field(field_name="item_name", datatype=DataType.VARCHAR, max_length=65535)
schema.add_field(field_name="dense_vector", datatype=DataType.FLOAT_VECTOR, dim=1024)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
# 3. 构建索引参数
index_params = client.prepare_index_params()
# 稠密向量索引:HNSW + COSINE
index_params.add_index(
field_name="dense_vector",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 200}
)
# 稀疏向量索引:SPARSE_INVERTED_INDEX + IP
index_params.add_index(
field_name="sparse_vector",
index_type="SPARSE_INVERTED_INDEX",
metric_type="IP",
params={"inverted_index_algo": "DAAT_MAXSCORE", "normalize": True, "quantization": "none"}
)
# 4. 创建集合
client.create_collection(collection_name=collection_name, schema=schema, index_params=index_params)
关键设计决策:
• auto_id=True:主键自增,无需业务侧生成 • enable_dynamic_field=True:支持动态字段,Schema 更灵活 • HNSW + COSINE:目前性能最好的图索引,适合稠密向量近似最近邻搜索,数据量大精度要求不高可以选择 IVF • SPARSE_INVERTED_INDEX + IP:稀疏向量专用倒排索引,内积度量兼顾语义匹配度和关联强度
步骤 4:数据插入
幂等性设计:插入前先删除同 item_name 的旧数据,避免重复:
# 1. 加载集合(必须加载后才能删除/查询)
client.load_collection(collection_name=collection_name)
# 2. 删除同名商品历史数据
safe_item_name = escape_milvus_string(item_name)
filter_expr = f'item_name=="{safe_item_name}"'
client.delete(collection_name=collection_name, filter=filter_expr)
# 3. 构造数据并插入
data = {
"file_title": file_title,
"item_name": item_name,
"dense_vector": dense_vector,
"sparse_vector": normalize_sparse_vector(sparse_vector)
}
client.insert(collection_name=collection_name, data=[data])
# 4. 再次加载,确保数据立即可查
client.load_collection(collection_name=collection_name)
注意:Milvus 的
delete和insert操作后需要load_collection()才能立即生效。
步骤 5:混合检索(Hybrid Search)
封装混合搜索能力:
from pymilvus import AnnSearchRequest, WeightedRanker
query_text = "white headphones, quiet and comfortable"
query_dense_vector = generate_dense_vector(768)
# 1. 构建双向量搜索请求
# 稠密向量,匹配语义
search_param_1 = {
"data": [query_dense_vector], # 向量/文本
"anns_field": "text_dense", # 字段
"param": {"metric_type": "cosine"}, # 配置项
"limit": 2 # 查询的条数
}
request_1 = AnnSearchRequest(**search_param_1)
# 稀疏向量,匹配关键字
search_param_2 = {
"data": [query_text],
"anns_field": "text_sparse",
"param": {"metric_type": "IP"},
"limit": 2
}
request_2 = AnnSearchRequest(**search_param_2)
reqs = [request_1, request_2, request_3]
# 2. 加权排名
rerank = WeightedRanker(0.5, 0.5, norm_score=True) # 开启归一化
res = client.hybrid_search(
collection_name=collection_name,
reqs=reqs,
ranker=rerank, # 负责合并和重排序
limit=2, # 最终要合并的条数,通常跟搜索条数保持一致
output_fields=["item_name"]
)
返回结果:
# 基本搜索返回的是双层 list,对应多个结果,混合检索返回一个结果。
["['id: 1, distance: 0.006047376897186041, entity: {}', 'id: 2, distance: 0.006422005593776703, entity: {}']"]
# "distance": 相似度的分数,0-1,越大越相似
# "entity": 匹配的实体其他的列
常用算法
WeightedRanker:加权排名器,给不同搜索方式的"打分"分配权重,加权求和后重新排名。
RRF Ranker 重排序算法:倒数(1 ÷ 这个数)排名融合,对同一个结果在不同搜索方式中的排名取倒数,然后相加重新排序。
| 特性 | RRF | WeightedRanker |
|---|---|---|
| 输入 | 只看排名(第几名) | 看原始分数(distance 值) |
| 好处 | 不考虑不同搜索的分数量级差异 | 能保留原始相似度信息 |
| 问题 | 丢掉了"这个产品到底多匹配"的信息 | 不同搜索的分数量级可能差异很大 |
四、核心设计点
| 设计点 | 说明 |
|---|---|
| 单例客户端 | 全局复用 MilvusClient,避免重复连接开销 |
| 自动建表 | 集合不存在时自动创建 Schema + 索引,零运维 |
| 幂等写入 | 按 item_name 先删后插,保证多次执行结果一致 |
| 双向量存储 | 稠密向量(语义)+ 稀疏向量(关键词),兼顾泛化与精确 |
| 混合检索 | WeightedRanker 融合双路召回结果,提升检索准确性 |
| 主键回填 | 自增 chunk_id 回填到业务数据,供下游关联使用 |
| 异常隔离 | 所有 Milvus 操作包裹 try-except,失败不中断主流程 |
五、可视化
通过部署 Attu 对 Milvus 进行可视化操作。如果 Milvus 用 Docker 跑的,Attu 可以一起配。
services:
milvus:
image: milvusdb/milvus:latest
ports:
- "19530:19530"
attu:
image: zilliz/attu:latest
ports:
- "8000:3000"
environment:
MILVUS_URL: milvus
depends_on:
- milvus
然后 docker-compose up -d 一键启动。