从零开始学Milvus:RAG工程师必备的向量数据库完全指南
文章目录
前言:为什么RAG离不开向量数据库?
在学习RAG(检索增强生成)的过程中,你一定听过这样的流程:文档 → 切分 → 向量化 → 存入向量库 → 检索相似片段 → 送入大模型。
整个流程的核心枢纽,就是向量数据库。它负责存储海量向量,并在毫秒级返回最相似的Top-K结果。没有高效的向量检索,RAG就无从谈起。
而Milvus,是目前开源界最主流、生态最完善、性能最强的向量数据库之一,也是RAG项目的首选方案。
本文将带你系统掌握Milvus,读完你将:
- 彻底理解向量数据库的核心概念
- 搞懂Milvus的内部架构与工作流程
- 吃透常见向量索引的原理与选型
- 能够独立完成Milvus的部署与CRUD
- 清楚它在RAG架构中扮演的角色
一、基础概念扫盲
1.1 什么是向量(Embedding)
向量,也叫嵌入向量(Embedding),是将文本、图片、音频等非结构化数据转化为的一组浮点数数组。
举个例子:
"苹果" → [0.12, -0.34, 0.56, ..., 0.78] (1024维)
语义相近的内容,在向量空间中距离也更近。向量数据库就是利用这个特性,实现"语义搜索"。
1.2 什么是向量数据库
传统数据库存的是结构化数据(字符串、数字),用精确匹配或模糊查询;
向量数据库存的是高维向量,用相似度计算来检索。
它解决的核心问题:
给定一个查询向量,在百万/亿级向量中,快速找到最相似的N个向量
如果不用向量数据库,你只能逐条计算距离(暴力搜索),数据量大了之后慢到无法接受。
1.3 Milvus是什么
Milvus 是一款开源的云原生向量数据库,2019年由Zilliz公司开源,目前是LF AI & Data基金会的毕业项目。
核心特点:
- 性能强悍:亿级向量毫秒级检索
- 云原生:存算分离架构,支持弹性扩缩容
- 功能丰富:支持标量过滤、多向量、分区、TTL等
- 生态完善:支持Python/Java/Go等多语言SDK,与LangChain、LlamaIndex深度集成
二、Milvus核心概念详解
这部分是重中之重,建议反复理解。
2.1 Collection(集合)
Collection 是Milvus中最顶层的数据组织单元,相当于关系型数据库中的"表"。
每个Collection有自己的Schema(结构定义),包含字段定义、主键、向量字段等。
2.2 Partition(分区)
Partition 是Collection内部的逻辑分片,相当于"分表"。
- 一个Collection可以包含多个Partition
- 插入数据时可以指定存入哪个Partition
- 检索时可以指定只搜某些Partition,大幅提升性能
典型使用场景:按时间分区(按月/按天),检索时只查最近3个月的数据。
2.3 Field(字段)
Field就是Collection中的"列",主要分两类:
| 字段类型 | 说明 | 示例 |
|---|---|---|
| 标量字段 | 传统数据类型,用于过滤 | INT64、VARCHAR、BOOL、FLOAT、JSON |
| 向量字段 | 存储向量,用于相似度检索 | FLOAT_VECTOR、BINARY_VECTOR |
注意:一个Collection可以有多个向量字段,但至少要有一个向量字段才能建索引。
2.4 Entity(实体)
Entity 就是一行数据,包含主键、各个标量字段和向量字段。相当于关系型数据库的"行记录"。
2.5 Index(索引)
索引是向量数据库的灵魂。它是基于向量字段构建的数据结构,目的是把暴力搜索变成近似搜索,以牺牲极小精度换取百倍千倍的速度提升。
没有索引的向量字段,只能走暴力搜索(FLAT),数据量大了会非常慢。
2.6 Segment(段)
Segment 是Milvus物理存储的最小单元,是内部概念,用户感知不强但很重要。
- 数据写入时先进入内存,达到一定大小后落盘成一个Segment
- 一个Partition由多个Segment组成
- 索引是按Segment构建的
理解了这些,你就掌握了Milvus的"数据组织世界观":
Collection(集合)
└── Partition(分区)
└── Segment(段)
└── Entity(实体)
├── 主键字段
├── 标量字段(可多个)
└── 向量字段(可多个)
三、Milvus系统架构
Milvus采用经典的存算分离 + 微服务架构,整体分为四层。
3.1 四层架构详解
第一层:接入层
- 对外提供统一的gRPC/HTTP接口
- 负责连接管理、鉴权、限流
- 对应组件:Proxy(无状态,可水平扩展)
第二层:协调服务层
大脑,负责元数据管理和任务调度,包含四个Coordinator:
- Root Coord:集群元数据管理、DDL操作(建删Collection)
- Data Coord:数据节点调度、Segment管理
- Query Coord:查询节点调度、负载均衡
- Index Coord:索引任务调度、索引节点管理
第三层:工作节点层
干活的执行节点,三类节点各司其职:
- Data Node:处理数据写入,构建Segment并落盘
- Query Node:加载Segment到内存,执行向量检索
- Index Node:异步构建向量索引
第四层:存储层
持久化存储,分为三类存储:
- 元数据存储:etcd,存Schema、节点状态等
- 消息存储:Pulsar/Kafka,数据写入的消息队列
- 对象存储:S3/MinIO,存向量数据、索引文件
3.2 架构总览
这种架构的好处是各组件独立扩展——查询压力大就加Query Node,写入压力大就加Data Node,非常灵活。
四、核心工作流程(流程图详解)
这是理解Milvus运行原理的关键。
4.1 数据写入流程
Milvus的写入是异步流式的,不是直接写磁盘。
关键点:
- 写入是异步的,SDK返回成功不代表数据立即可检索
- 数据先写消息队列,再由Data Node消费落盘
- 新数据从写入到可查通常有秒级延迟(近实时)
4.2 向量检索流程
关键点:
- 检索是分布式的,每个Query Node只搜自己加载的Segment
- Proxy负责全局合并与排序,保证结果准确性
- 支持标量过滤(先过滤再检索,或先检索再过滤)
五、向量索引原理详解
索引是向量数据库最核心的技术,也是面试高频考点。Milvus支持多种索引类型,各有优劣。
5.1 FLAT - 暴力搜索
原理:不构建索引,查询时与所有向量逐一计算距离。
- 优点:精度100%,内存占用小
- 缺点:速度极慢,只适合万级以下小数据量
- 适用场景:测试、小数据集、追求百分百召回率
5.2 IVF_FLAT - 倒排文件索引
原理:基于聚类的倒排索引。
- 先对所有向量做K-Means聚类,得到nlist个聚类中心
- 查询时,先找到距离最近的nprobe个聚类
- 只在这nprobe个聚类内做暴力搜索
- 优点:速度快,参数可调,平衡精度与性能
- 缺点:聚类需要训练时间,高维下性能下降
- 关键参数:
nlist(聚类中心数)、nprobe(查询时探查的聚类数)
经验值:nlist一般设为
sqrt(数据量),nprobe越大精度越高、速度越慢。
5.3 IVF_SQ8 - 标量量化
原理:在IVF基础上,将每个向量维度从float32压缩为uint8(标量量化),体积压缩约4倍。
- 优点:内存占用极低,速度比IVF_FLAT更快
- 缺点:有精度损失
- 适用场景:海量数据、内存有限、对精度要求不极致
5.4 HNSW - 层次化导航小世界
目前最主流、综合性能最好的索引类型。
原理:构建多层有向图,上层稀疏、下层稠密。查询时从上层入口出发,逐层向下导航,最终在底层找到最近邻。
- 优点:查询速度极快,召回率高,不需要训练
- 缺点:构建索引慢,内存占用大
- 关键参数:
M(每层每个节点的连接数,默认16)、efConstruction(构建时搜索范围,默认200)、ef(查询时搜索范围)
经验:M一般取8~64,维度越高M可以适当调大;ef越大精度越高速度越慢。
5.5 索引选型指南
| 索引类型 | 召回率 | 速度 | 内存占用 | 构建速度 | 推荐场景 |
|---|---|---|---|---|---|
| FLAT | 100% | 最慢 | 低 | - | 万级以下数据 |
| IVF_FLAT | 高 | 中等 | 中等 | 中等 | 百万级,平衡型 |
| IVF_SQ8 | 中 | 快 | 很低 | 中等 | 千万级,内存紧张 |
| HNSW | 很高 | 最快 | 高 | 慢 | 千万级,追求极致查询性能 |
RAG项目首选:HNSW,因为RAG对查询延迟要求高,且数据量通常在千万级以内。
六、Milvus快速上手实战
6.1 Docker 单机部署
最简单的入门方式,使用Docker Compose一键启动:
# 下载docker-compose文件
wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
# 启动
docker-compose up -d
启动后,Milvus会监听 19530 端口(gRPC)。
6.2 Python SDK 基本操作
安装SDK:
pip install pymilvus
步骤1:连接Milvus
from pymilvus import connections, utility
connections.connect(
alias="default",
host="localhost",
port="19530"
)
步骤2:创建Collection
from pymilvus import CollectionSchema, FieldSchema, DataType, Collection
# 定义字段
book_id = FieldSchema(name="book_id", dtype=DataType.INT64, is_primary=True, auto_id=True)
book_name = FieldSchema(name="book_name", dtype=DataType.VARCHAR, max_length=200)
word_count = FieldSchema(name="word_count", dtype=DataType.INT64)
embedding = FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
# 定义Schema
schema = CollectionSchema(
fields=[book_id, book_name, word_count, embedding],
description="图书知识库",
enable_dynamic_field=True # 开启动态字段,灵活扩展
)
# 创建Collection
collection = Collection(name="book_kb", schema=schema)
步骤3:插入数据
import numpy as np
# 模拟数据
data = [
["深度学习入门", "自然语言处理实战", "向量数据库指南"], # book_name
[50000, 80000, 30000], # word_count
np.random.random([3, 768]).tolist() # 向量,实际项目中用Embedding模型生成
]
# 插入
mr = collection.insert(data)
print(f"插入了 {mr.insert_count} 条数据")
步骤4:创建HNSW索引
index_params = {
"metric_type": "COSINE", # 距离度量:L2 / IP / COSINE
"index_type": "HNSW",
"params": {
"M": 16,
"efConstruction": 200
}
}
collection.create_index(
field_name="embedding",
index_params=index_params
)
距离度量选型:文本相似度推荐 COSINE 或 IP(内积),向量归一化后两者等价。
步骤5:加载并检索
# 将Collection加载到内存(必须加载才能检索)
collection.load()
# 查询向量
search_vector = np.random.random([1, 768]).tolist()
search_params = {
"metric_type": "COSINE",
"params": {"ef": 64}
}
# 执行检索
results = collection.search(
data=search_vector,
anns_field="embedding",
param=search_params,
limit=3, # Top-K
expr="word_count > 40000", # 标量过滤条件
output_fields=["book_name", "word_count"] # 返回的字段
)
# 打印结果
for hits in results:
for hit in hits:
print(f"ID: {hit.id}, 书名: {hit.entity.get('book_name')}, 相似度: {hit.score}")
七、Milvus在RAG中的完整应用
7.1 RAG全流程图
7.2 Milvus在RAG中的核心价值
- 语义检索:基于向量相似度找到语义相关的文档片段
- 混合检索:支持标量字段过滤(如按文档来源、时间过滤)
- 高性能:知识库即使扩充到百万级,检索依然毫秒级
- 增量更新:支持随时新增、删除、修改知识库内容
7.3 一个最小RAG检索函数
def rag_retrieve(question: str, top_k: int = 3):
"""
RAG检索阶段:将问题向量化,从Milvus中检索最相关的文档片段
"""
# 1. 将问题转为向量(实际用OpenAI/BGE等Embedding模型)
question_vector = embedding_model.encode(question).tolist()
# 2. Milvus检索
results = collection.search(
data=[question_vector],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=top_k,
output_fields=["content", "source"]
)
# 3. 提取检索到的文本片段
contexts = []
for hit in results[0]:
contexts.append({
"content": hit.entity.get("content"),
"source": hit.entity.get("source"),
"score": hit.score
})
return contexts
拿到contexts后,拼接进Prompt送入大模型,就完成了完整的RAG流程。
八、最佳实践与常见坑
8.1 Collection设计建议
- 主键尽量用
INT64,性能比VARCHAR好 - 维度不要超过4096,越高维检索越慢、索引效果越差
- 善用Partition做时间分片,避免全量检索
- 开启动态字段(
enable_dynamic_field=True),方便后续加字段
8.2 性能优化建议
- 数据量小于100万,HNSW + COSINE 是黄金组合
- 查询时合理设置
ef,兼顾精度与速度 - 检索时尽量加过滤条件,减少扫描范围
- 定期做
compact,清理已删除数据,优化Segment
8.3 常见问题
Q:插入数据后搜不到?
A:Milvus写入是近实时的,默认1秒刷新一次;可调用 collection.flush() 强制落盘。
Q:检索结果不准怎么办?
A:调大 nprobe(IVF系列)或 ef(HNSW),牺牲速度换精度;检查Embedding模型质量。
Q:内存不够用怎么办?
A:换IVF_SQ8索引,或开启磁盘索引(DiskANN),用磁盘换内存。
结语
Milvus是RAG技术栈中不可或缺的一环。理解了它的核心概念、架构原理和索引机制,你才能在实际项目中做出合理的技术选型和性能调优。
本文覆盖了入门到实战的完整路径,但Milvus的深度远不止于此——还有标量索引、事务、多租户、冷热分层等进阶特性等待你探索。
更多推荐
所有评论(0)