Milvus:为大规模、高性能而生的企业级向量数据库
- Milvus:为大规模、高性能而生的企业级向量数据库
-
- 一、亿级向量的主流方案
- 二、Milvus原理与基础
-
- [2.1 Milvus 解决了什么问题](#2.1 Milvus 解决了什么问题)
- [2.2 整体架构:四层分离的设计哲学](#2.2 整体架构:四层分离的设计哲学)
- [2.3 数据组织:Collection → Partition → Segment](#2.3 数据组织:Collection → Partition → Segment)
- [2.4 索引原理:三种主流 ANN 索引](#2.4 索引原理:三种主流 ANN 索引)
-
- [2.4.1 IVF(Inverted File)------ 基于聚类的倒排](#2.4.1 IVF(Inverted File)—— 基于聚类的倒排)
- [2.4.2 HNSW(Hierarchical Navigable Small World)------ 基于图的导航](#2.4.2 HNSW(Hierarchical Navigable Small World)—— 基于图的导航)
- [2.4.3 ANNOY ------ 基于树的分割](#2.4.3 ANNOY —— 基于树的分割)
- [2.5 一致性与隔离级别](#2.5 一致性与隔离级别)
- 三、方案与对比
-
- [3.1 ChromaDB vs Milvus:何时升级](#3.1 ChromaDB vs Milvus:何时升级)
- [3.2 索引类型选型](#3.2 索引类型选型)
- 四、实战步骤
-
- [4.1 step 1:部署模式选择](#4.1 step 1:部署模式选择)
- [4.2 step 2:关键代码(Python SDK)](#4.2 step 2:关键代码(Python SDK))
- [4.3 step 3:插入与检索](#4.3 step 3:插入与检索)
- [4.4 step 4:关键配置(生产避坑)](#4.4 step 4:关键配置(生产避坑))
- [4.5 step 5:验证手段](#4.5 step 5:验证手段)
- 五、总结
- 系列导航
- 参考资料
Milvus:为大规模、高性能而生的企业级向量数据库
当向量规模从百万级迈向十亿级,单机数据库的"内存装不下、CPU 算不动"会成为第一个拦路虎。
一、亿级向量的主流方案
让我们从问题出发:
1、我们要"在十亿级别的商品向量里,为一个用户实时找出最相似的 100 个商品"这个方法最关键的部分是什么?
- 最关键的部分是分布式架构 + 高效索引。十亿向量无法装入单机内存,必须把数据切片到多台机器;与此同时,要在毫秒级返回结果,必须借助近似最近邻(ANN)索引。
2、当流量从 1k QPS 涨到 100k QPS 时,向量检索服务如何不被打垮?
- 需要计算与存储分离 + 多副本 + 一致性可调。在请求高峰时扩出更多 QueryNode 处理查询请求,而数据本身只需在多个副本间复制即可。
3、当某个查询节点宕机时,正在处理的请求怎么办?数据会不会丢?
- 需要故障转移 + 多副本机制。Milvus 通过 etcd 做服务发现、K8s 调度 + 健康检查做故障转移、Pulsar/Kafka 做写入恢复,保证节点宕机不影响查询和数据不丢。
在实际生产中,向量数据规模往往远超预期:电商商品、日志、文档、图片、用户行为,每一类动辄千万起步。当我们把 ChromaDB 这类轻量库用到一定规模时,"插入变慢""查询超时""内存爆掉"会接踵而来。Milvus 正是为这种场景而生的解决方案。
官方文档:https://milvus.io/docs/zh/overview.md
二、Milvus原理与基础
Milvus 是由 Zilliz 公司创建并贡献给 LF AI & Data 基金会的开源向量数据库。它的设计目标非常明确:处理十亿乃至万亿级向量、毫秒级响应、企业级可靠性。要理解这三点,我们先从架构开始拆解。
2.1 Milvus 解决了什么问题
-
大规模向量数据的存储和检索 :当数据量达到数十亿甚至更多时,单节点数据库会遇到性能瓶颈。Milvus 采用分布式架构,可以将计算和存储资源分开,并根据需求进行水平扩展,从而轻松应对海量数据的挑战。
-
高并发和低延迟的查询需求:对于需要同时处理成千上万用户请求的在线服务(例如电商的推荐系统),Milvus 能够通过其分布式的特性,保证在高并发下依然有非常低的查询延迟。
-
企业级的可靠性和高级功能:Milvus 提供了数据备份、数据副本、故障转移和可调的一致性级别等高级功能,确保了生产环境下的高可用性和数据可靠性。
2.2 整体架构:四层分离的设计哲学
Milvus 自 2.0 之后采用了存算分离的微服务架构。理解这张图,是后续做容量规划、问题排查的前提。
┌─────────────────────────────────────────────────────┐
│ Access Layer(接入层) │
│ - Proxy:无状态网关,负责请求路由、鉴权、限流 │
├─────────────────────────────────────────────────────┤
│ Coordinator Service(协调层) │
│ - Root Coord:管理集合(Collection)元数据 │
│ - Query Coord:管理查询节点、负载均衡、Segment 分配 │
│ - Data Coord:管理数据节点、数据写入、Compaction │
│ - Index Coord:管理索引构建任务 │
├─────────────────────────────────────────────────────┤
│ Worker Node(执行层) │
│ - QueryNode:执行向量检索与标量过滤 │
│ - DataNode:处理写入、构建日志索引 │
│ - IndexNode:异步构建向量索引 │
├─────────────────────────────────────────────────────┤
│ Storage Layer(存储层) │
│ - Meta Store (etcd):元数据 │
│ - Log Broker (Pulsar/Kafka):写入日志 │
│ - Object Storage (S3/MinIO):向量与索引的持久化 │
└─────────────────────────────────────────────────────┘
我们来逐层解读这张图:
- 接入层是无状态的 Proxy,可以无限水平扩展------这是应对百万 QPS 的关键。
- 协调层 是"大脑",4 个 Coord 各司其职,通过 etcd 选主。etcd 挂了整个集群就瘫了,所以生产环境必须部署 3 节点 etcd 集群。
- 执行层做实际工作。QueryNode 和 DataNode 也是无状态的,K8s 拉起 / 杀掉的代价很低。
- 存储层 用对象存储(S3/MinIO)存真正的向量数据。这与 Elasticsearch 的设计哲学类似------让内存和 CPU 去服务查询,磁盘做冷数据兜底。
注意:Milvus 的存算分离不是"为了分离而分离",而是因为向量检索的查询负载(计算密集)和写入负载(IO 密集)特征完全不同。把它们解耦后,扩缩容策略可以独立设计。
2.3 数据组织:Collection → Partition → Segment
Milvus 把数据组织成三层结构,这是排查"为什么查得慢""为什么插入卡住"的基础。
| 层级 | 概念 | 作用 | 类比 |
|---|---|---|---|
| Collection | 业务表 | 一类向量的容器,如 product_embeddings |
关系数据库的 Table |
| Partition | 分区 | 按业务维度切分数据,如按时间/类别 | 分区表 |
| Segment | 段 | 物理存储单元,达到阈值后封存、构建索引 | LSM-Tree 的 SSTable |
关键认知:
- 数据写入时 进入 Growing Segment(内存中),暂不建索引,以加速写入。
- Growing Segment 达到阈值(默认 512MB)后封存 为 Sealed Segment,异步构建索引。
- Sealed Segment 不可变 ,只能在后台做 Compaction (合并小段)和 Index Rebuild(重建索引)。
这就是为什么 Milvus 写入后立即查询可能会比较慢------数据还在 Growing Segment 里走暴力扫描。生产环境通常会预热(force flush)或容忍短暂的查询不全。
2.4 索引原理:三种主流 ANN 索引
向量检索的核心是近似最近邻(Approximate Nearest Neighbor, ANN) 算法。Milvus 支持多种索引,我们重点看三类。
2.4.1 IVF(Inverted File)------ 基于聚类的倒排
三步法理解 IVF_FLAT:
step 1:聚类------用 K-Means 把所有向量聚成 nlist 个簇,每个簇有一个中心点。
step 2:分配------每个向量归属到距离最近的簇,记录"我属于哪个簇"。
step 3:检索------查询时先找最近的 nprobe 个簇,只在这些簇的向量里做精确距离计算,跳过其余向量。
查询向量 → 计算到 nlist 个簇心的距离
→ 选最近的 nprobe 个簇(nprobe << nlist)
→ 只在这 nprobe 个簇里做精确 KNN
nlist 与 nprobe 是关键参数:
- nlist 越大 → 簇越多 → 单簇越小 → 检索越快,但召回率可能下降
- nprobe 越大 → 扫描的簇越多 → 召回率越高,但延迟增加
2.4.2 HNSW(Hierarchical Navigable Small World)------ 基于图的导航
HNSW 是当前最流行的图索引,可以理解为"向量世界的跳表"。
核心思想:构建一张多层图,每层的节点是向量的子集,层数越高节点越稀疏。查询时从最稀疏的顶层开始,每层贪心搜索找到局部最近点,然后下钻到下一层继续搜索。
Layer 2: [A] ------------------------------ [Z] ← 顶层:导航
Layer 1: [A] --- [M] --- [R] --- [Z] ← 中层:粗定位
Layer 0: [A][B][C] ... [M][N][O] ... [Z] ← 底层:精确搜索
优势 :查询速度极快(O(log N)),召回率高。
劣势:构建慢、内存占用大(需把图全装入内存)。
2.4.3 ANNOY ------ 基于树的分割
ANNOY 用多棵随机二叉树把空间切分,查询时在每棵树里找最近点后聚合。Milvus 已逐步弃用 ANNOY,实际生产推荐 HNSW 或 IVF。
2.5 一致性与隔离级别
Milvus 的"分布式"不是"主从复制"那么简单,它在写入可见性上提供四种一致性级别------这是面试常考点。
| 级别 | 含义 | 延迟 | 一致性 |
|---|---|---|---|
| Strong | 能读到最新写入 | 高 | 强 |
| Session | 同一会话内保证单调读 | 中 | 中 |
| Bounded | 容忍短暂延迟(默认) | 低 | 弱 |
| Eventually | 最终一致 | 极低 | 最弱 |
默认是 Bounded (bounded_staleness=5s),平衡了一致性与延迟。强一致必然牺牲性能------这与 CAP 定理一脉相承。
三、方案与对比
3.1 ChromaDB vs Milvus:何时升级
前面文章我们讨论了 ChromaDB 的轻量与易用。那么问题来了:什么时候必须升级到 Milvus? 让我们用一个对比表回答。
| 特性 | ChromaDB | Milvus |
|---|---|---|
| 核心优势 | 简洁易用、开发速度快 | 高性能、高可扩展、功能丰富 |
| 架构 | 单体架构,可嵌入式运行 | 分布式架构,计算存储分离 |
| 数据规模 | 适合百万级向量 | 适合十亿级甚至万亿级向量 |
| 延迟(百万级) | 毫秒级 | 毫秒级 |
| 延迟(十亿级) | 不支持 / 性能崩溃 | 仍可保持毫秒~十毫秒级 |
| 适用场景 | 快速原型验证、中小项目、个人开发 | 企业级应用、大规模推荐系统、搜索引擎 |
| 部署复杂度 | 一行 pip install 即可 |
需 Docker Compose / K8s 部署多个组件 |
| 运维成本 | 几乎为零 | 需要运维 etcd / MinIO / Pulsar 等依赖 |
| 索引类型 | 默认 HNSW(固定) | IVF / HNSW / PQ / DiskANN 等 10+ 种 |
| 多租户隔离 | 不支持 | 支持 Partition / Database 隔离 |
| 混合查询 | 弱 | 标量过滤 + 向量检索 + 全文检索(2.4+) |
选型有立场:
- 百万级以内、原型验证、学习阶段 → 选 ChromaDB:一行命令启动,5 分钟跑通 RAG。
- 千万到亿级、追求功能丰富但不想自建 → 选 Zilliz Cloud(Milvus 云服务):免运维,按量付费。
- 十亿级以上、金融级可靠性要求、有 K8s 团队 → 选 Milvus 自建集群:可控性最强。
为什么 ChromaDB 撑不住十亿级?因为它把向量全装进内存并用单进程做检索。十亿 768 维 float32 向量 ≈ 3TB 内存,单机物理极限就过不去。Milvus 用磁盘 + 内存分层 + 分布式,把"内存装不下"的问题转化成了"我可以横向扩"。
3.2 索引类型选型
确定用 Milvus 后,下一个关键决策是选哪种索引。这是性能调优的"第一颗扣子"。
| 索引类型 | 构建速度 | 查询速度 | 召回率 | 内存占用 | 适用场景 |
|---|---|---|---|---|---|
| FLAT(暴力) | 极快 | 慢 | 100% | 高 | 基线测试、<10w 向量 |
| IVF_FLAT | 中 | 中 | 95%+ | 中 | 百万级、平衡场景 |
| IVF_PQ | 中 | 快 | 90%+ | 低(压缩) | 内存紧张、十亿级 |
| HNSW | 慢 | 极快 | 98%+ | 极高 | 低延迟、高召回场景 |
| DISKANN | 中 | 中 | 95%+ | 极低 | 超大规模、SSD 充足 |
选型有立场:
- 追求极致召回率、不在乎内存 → 选 HNSW:M=16, efConstruction=200 是常用起点。
- 内存有限、规模上亿 → 选 IVF_PQ:PQ 压缩能把内存降一个数量级。
- 数据量超 10 亿 + SSD 充足 → 选 DISKANN:把索引放磁盘,内存只放热数据。
实际生产中召回率是底线 ------90% 召回率在 RAG 场景下意味着 10% 的查询会"找不到正确答案",这种损失往往是业务不可接受的。先用 FLAT 跑基线召回率,再压索引。
四、实战步骤
下面我们一起从零跑通一个 Milvus 集群。虽然不能贴完整可运行项目(避免长代码淹没主线),但关键步骤和"踩过的坑"会一一标注。
4.1 step 1:部署模式选择
Milvus 提供三种部署模式,对应不同的场景:
| 模式 | 适用阶段 | 组件数 | 一句话 |
|---|---|---|---|
| Milvus Lite | 本地开发、学习 | 1 | Python pip install milvus-lite,嵌入式 |
| Standalone | 中小规模生产 | 1 进程(含所有组件) | Docker Compose 一键起 |
| Cluster | 大规模生产 | 多个微服务 | K8s Helm Chart 部署 |
推荐路径:本地用 Lite 验证 → 小流量用 Standalone → 流量上量后切 Cluster。三者使用相同的 SDK,迁移成本几乎为零。
4.2 step 2:关键代码(Python SDK)
以下是连接 Milvus、创建 Collection、插入数据、检索的核心代码骨架。重点看注释里的"坑"。
python
from pymilvus import MilvusClient, DataType
# 1. 连接(注意:MilvusClient 是新版 SDK,旧版用 connections.connect())
client = MilvusClient(uri="http://localhost:19530")
# 2. 创建 Collection
schema = client.create_schema(
auto_id=False, # 手动指定主键,便于幂等
enable_dynamic_field=True # 允许未定义的字段(调试期好用,生产建议关)
)
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=768)
schema.add_field(field_name="category", datatype=DataType.VARCHAR, max_length=64)
# 3. 配置索引(关键:metric_type 与算法强相关)
index_params = client.prepare_index_params()
index_params.add_index(
field_name="vector",
index_type="HNSW", # 索引类型
metric_type="COSINE", # 距离度量:欧氏/内积/余弦
params={"M": 16, "efConstruction": 200} # HNSW 参数
)
client.create_collection(
collection_name="product_emb",
schema=schema,
index_params=index_params,
consistency_level="Bounded" # 默认级别,平衡性能与一致性
)
我们来逐段解读这段代码:
enable_dynamic_field=True允许插入"未在 schema 中定义"的字段。生产环境务必关闭------一旦打开,相当于把"字段约束"这一数据质量护栏拆了。metric_type是最容易踩坑 的地方:- 归一化后的 embedding(如 BGE、OpenAI)→ 用
IP(内积)或COSINE(余弦) - 未归一化的特征向量 → 用
L2(欧氏距离) - 混用会导致召回率莫名其妙地低,而且代码不会报错。
- 归一化后的 embedding(如 BGE、OpenAI)→ 用
consistency_level="Bounded"是默认选项。如果你要做"插入后立即查询" (如 RAG 的实时数据入库),改用Strong,否则可能查到旧数据。
4.3 step 3:插入与检索
python
# 插入(注意:HNSW 不支持实时构建索引,数据会先进 Growing Segment)
data = [
{"id": 1, "vector": [...], "category": "phone"},
{"id": 2, "vector": [...], "category": "laptop"},
]
client.insert(collection_name="product_emb", data=data)
# 检索 + 标量过滤(混合查询是 Milvus 的杀手锏)
results = client.search(
collection_name="product_emb",
data=[query_vector], # 查询向量
filter="category == 'phone'", # 标量过滤
limit=10,
output_fields=["id", "category"], # 返回字段
search_params={"ef": 100} # HNSW 检索参数
)
关键点解读:
filter参数支持类 SQL 表达式(==,in,>,and),这是 ChromaDB 做不到的。生产中"在某个分类下找相似商品"这类业务非常依赖它。search_params={"ef": 100}控制 HNSW 检索时的图遍历深度。ef 越大召回越高、延迟也越大 ,生产环境建议从ef=64起步压测。- 首次插入后立即 search 可能数据不全 ,因为 Growing Segment 还没被检索调度加载到 QueryNode。可用
client.flush()强制封存(生产慎用,会卡写入)。
4.4 step 4:关键配置(生产避坑)
| 配置项 | 推荐值 | 含义 | 踩坑提醒 |
|---|---|---|---|
queryNode.replicas |
2-3 | 查询节点副本数 | 副本越多查询并发越高,但内存翻倍 |
dataCoord.segment.maxSize |
512MB | Segment 封存阈值 | 太小会增加 Compaction 频率,太大会拖慢查询 |
queryNode.segcore.mmap.enable |
true(亿级以上) | 启用 mmap 内存映射 | 节省内存,但首次查询会触发缺页中断 |
index.params.M |
16 | HNSW 每节点连接数 | M 越大召回越高、内存越大 |
index.params.efConstruction |
200 | HNSW 构建时搜索深度 | 越大索引质量越好、构建越慢 |
quotaConfig.middle.*Protection.enabled |
true | 内存/磁盘保护 | 防止 OOM 但会拒绝写入 |
提醒:以上参数都不是"越大越好" 。生产调优一定要带"召回率 + 延迟 + 内存"三维监控做 trade-off,单维压测会骗自己。
4.5 step 5:验证手段
完成部署后,做三个最小验证:
- 健康检查 :
curl http://<host>:9091/healthz返回 200 → 组件就绪。 - 写入验证 :
insert一批数据 →query主键能查到 → 通过。 - 召回率验证 :用 FLAT 索引(100% 召回)跑一遍测试集,记录 top-K 结果;再用 HNSW 跑同样的测试集,召回率 ≥ 95% 方可上线。
反直觉点:HNSW 索引参数在数据量翻倍后需要重新调优。M、efConstruction、ef 都不是"一调永逸"的参数。
五、总结
让我们把核心结论提炼成三条:
- Milvus 的本质是"分布式 + 存算分离 + 多索引可插拔"。理解了四层架构和三段式数据组织,排查任何问题都有据可循。
- ChromaDB 适合原型,Milvus 适合生产。当数据量从百万迈向十亿、QPS 从百迈向十万,Milvus 几乎是国内中大厂的"标准答案"。
- Milvus 不是"装上就能用" 。索引选型、一致性级别、参数调优、Compaction 策略共同决定生产表现。强烈建议先用 Milvus Lite 跑通,再上 Standalone,最后切 Cluster。
适用边界:
- ✅ 适合:RAG 知识库、大规模推荐、图像/视频检索、广告投放、人脸识别
- ⚠️ 谨慎:< 10w 向量的轻量场景(ChromaDB 更经济)
- ❌ 不适合:纯事务型业务、需要强 JOIN 的关系查询(用 MySQL/PostgreSQL)
希望这篇文章能帮你从"知道 Milvus"走到"会用 Milvus"。下一篇文章,我们会一起动手搭建一个生产级的 Milvus + RAG 服务,把今天讲的架构、索引、参数调优全部串起来。
系列导航
向量数据库系列:从轻量到企业级,系统讲清选型与实战。
- 第 1 篇:Milvus:为大规模、高性能而生的企业级向量数据库(本文)
- 第 2 篇:待更新...