单项目深读

Milvus:把向量检索拆成可独立扩展的云原生分层

Milvus 用 Go 编写、Apache-2.0 许可,把向量检索拆成接入、协调、工作节点与存储四层,支持 HNSW、DiskANN 等索引,为 RAG 与推荐系统提供可横向扩展

|INS-0013

一个 RAG 应用从 demo 走到生产,最先撑不住的地方往往不是模型,是检索层。单机向量库在几百万条 embedding 上跑得好好的,到几亿条、每秒上万次查询、还要边写边读的时候,就开始掉链子。Milvus 从 2019 年就在赌这个断层会出现,并且用一套彻底分层的架构去接住它。

先看它把什么拆开了

README 里有一句关键的话:Milvus 把系统和核心向量检索引擎分离开。这句话是理解整个项目的钥匙。大多数向量库把「存向量、建索引、查 topK」做成一个进程里的几件事,Milvus 把它们拆成可以各自独立部署、独立扩容的角色。

按 README 描述的分布式架构,可以归纳成四层:

层角色职责扩容方式
接入层Proxy / SDK 入口接收 MilvusClient 的请求,做路由与结果聚合无状态,随请求量横向加
协调层Coordinator管理元数据、调度、负载均衡,负责集群一致性通过 Coordinator HA 做故障恢复
工作节点层Query Node / Data Node / Index Node分别承担查询、写入、建索引三类负载按读写比例分别增减
存储层对象存储 + 消息流持久化向量与日志,支撑实时流式更新依托 K8s 与对象存储弹性伸缩

这个拆法的直接后果写在 README 的 High Performance 段落里:读多的场景加 Query Node,写多的场景加 Data Node,两条曲线不用互相迁就。再加上 K8s 上的无状态微服务设计,节点挂了可以快速恢复;replica 机制把同一批数据段加载到多个 Query Node 上,同时提升容错和吞吐。

数据流转的路径大致是:写入先进消息流,由 Data Node 消费落盘;索引由 Index Node 单独构建,不阻塞查询;查询请求经 Proxy 到 Query Node,后者在已加载的数据段上做向量检索并合并结果。存储与计算分离,意味着计算节点可以是纯无状态的,扩容和故障恢复都变成调度问题而不是数据搬迁问题。

部署形态上,README 明确给了三档:分布式集群(K8s-native,面向十亿级向量和数万 QPS)、Standalone 单机模式(Docker 单机部署)、以及 Milvus Lite(pip install 就能跑的轻量版,适合 Python 快速起步)。同一套 API,从本地 .db 文件到集群,切换的只是连接串。

索引不是只有一种,这是分层的另一半

如果只做分层部署,Milvus 只是个能扩容的向量库。它真正的差异点在「系统与检索引擎分离」带来的索引可插拔性。README 列出的索引类型覆盖了不同取舍:

  • HNSW:图索引,召回与延迟平衡的常用选择
  • IVF 系列(含 IVFPQ 等量化变体):倒排 + 量化,压缩内存占用
  • FLAT:暴力检索,作为召回基准
  • SCANN、DiskANN:面向超大规模、把索引放到磁盘的场景
  • mmap:把数据映射到内存外,降低常驻内存压力
  • GPU 索引:README 点名支持 NVIDIA 的 CAGRA

同一套系统里能挂这么多种索引,前提就是前面说的分离——检索引擎是可替换的组件,不是焊死在主进程里的逻辑。README 还特别提到针对 metadata filtering 和 range search 做了优化,这两件事在纯向量检索里是性能陷阱:先过滤再检索会漏召回,先检索再过滤会浪费算力。Milvus 用 scalar index 处理标量字段,配合向量索引一起走。

从一行 pip 到一个 collection

README 的 Quickstart 给的是最短路径。先装 SDK:

bash
$ pip install -U pymilvus

想要完全本地、零服务端,装 Lite 变体,用一个文件路径当数据库:

python
from pymilvus import MilvusClient client = MilvusClient("milvus_demo.db")

连已经部署好的 Milvus 服务或 Zilliz Cloud,换成 uri + token:

python
client = MilvusClient( uri="<endpoint_of_self_hosted_milvus_or_zilliz_cloud>", token="<username_and_password_or_zilliz_cloud_api_key>")

建 collection、写数据、查 topK,三步走完:

python
client.create_collection( collection_name="demo_collection", dimension=768, # The vectors we will use in this demo have 768 dimensions ) res = client.insert(collection_name="demo_collection", data=data) query_vectors = embedding_fn.encode_queries(["Who is Alan Turing?", "What is AI?"]) res = client.search( collection_name="demo_collection", # target collection data=query_vectors, # a list of one or more query vectors, supports batch limit=2, # how many results to return (topK) output_fields=["vector", "text", "subject"], # what fields to return )

注意 search 的 data 支持批量查询向量,output_fields 决定返回哪些标量字段——这正是「向量 + 标量 + JSON」混合存储的体现。README 在 Why Milvus 里说得很直白:Milvus 存的不只是向量,还有整数、字符串、JSON 这些标量类型,检索时可以带 metadata filtering 或做 hybrid search。

除了稠密向量,还有稀疏向量这条路

RAG 场景里一个常见的争论是:语义检索够不够,还要不要关键词检索。Milvus 的答案是两个都要,而且放在同一个 collection 里。README 写明它原生支持以 BM25 做全文检索,也支持 SPLADE、BGE-M3 这类学习式稀疏 embedding;稀疏向量和稠密向量可以存在同一个 collection,再定义 function 对多个检索请求的结果做 rerank。

这带来的是 hybrid search 的实现方式:不是在外面拼两个系统再合并,而是在库内完成多路召回与重排。README 的教程表里,Hybrid Search、Full Text Search、Multimodal Search using Multi Vectors 都是这个能力的展开,Graph RAG 也在其中。

多租户和冷热分层:为「一个集群服务很多业务」设计

README 里容易被略过但很实用的一段是 multi-tenancy。Milvus 支持在 database、collection、partition、partition key 四个层级做隔离,官方说法是单集群可以承载从几百到几百万租户,同时保证检索性能和访问控制。对 SaaS 类产品,这意味着不必为每个客户起一套实例。

冷热存储是配套的成本手段:热数据放内存或 SSD 保性能,冷数据放更便宜慢速的存储。这套机制和前面的存储计算分离是一体的——存储层本来就在对象存储上,冷热只是加载策略的差别。

安全方面 README 列了三件:强制用户认证、TLS 加密、RBAC 基于角色的细粒度权限。这是企业采购清单上的常规项,但能在一个开源向量库里凑齐的不多。

和同主题项目比,它站在哪个位置

把素材里的对照项目摆在一起,定位差异比单看 Milvus 更清楚:

项目语言定位与 Milvus 的差别
MilvusGo / C++云原生分布式向量数据库分层架构、多索引、稀疏+稠密混合、多租户
QdrantRust高性能向量数据库与检索引擎同为专用向量库,Milvus 更强调分布式分层与索引种类
MeilisearchRust面向站点的搜索 API,关键词+向量混合偏应用侧搜索框,Milvus 偏底层向量存储与大规模检索
LlamaIndexPython文档处理与 RAG 框架是上层框架,通常把 Milvus 这类库当作检索后端
AnythingLLMJavaScript本地优先的 AI 应用工作区端到端产品,内置向量库是组件而非主角
PageIndexPython用推理代替向量检索的 RAG 索引路线相反:不要 embedding 和向量库,靠模型定位段落

这张表里最有意思的对照是 PageIndex。它主张用推理代替向量检索,免去 embedding 和向量数据库——这恰好是 Milvus 存在前提的反面。两者的分歧不在实现,在对「检索该由谁做」的判断:一边是把相似度计算做到极致并工程化,一边是把定位交给模型推理。Milvus 的赌注是规模和延迟,PageIndex 的赌注是推理质量和结构理解。真实系统里两者未必互斥,但选型时这是两条不同的路。

和 Qdrant 的对比更直接:都是专用向量数据库,都用系统语言写(Go/C++ vs Rust),都强调性能。Milvus 的差异化落在「拆得更多」——计算与存储分离、索引与系统分离、读写节点分离,代价是部署复杂度更高,收益是十亿级规模下的可扩展性。Qdrant 在这份素材里没有给出架构细节,不做过度推断。

LlamaIndex、AnythingLLM 这类不是竞品而是上下游:前者是 RAG 框架,后者是成品应用,Milvus 在它们的栈里是检索后端那一格。README 的教程表本身就在示范这种关系——RAG、语义搜索、推荐系统、多模态检索,都是把 Milvus 当底层存储用。

谁该用它,谁不必

README 的定位很清楚:为 scale 而生。如果你的向量规模在百万级以内、QPS 不高、团队不想维护 K8s 集群,Milvus Lite 或单机 Standalone 就够,pip install pymilvus[milvus-lite] 加一个 .db 文件路径就能开始,不需要碰分布式那一套。

当数据量往十亿走、读写负载形态开始分化、需要多租户隔离或冷热分层控成本时,分层架构的价值才兑现:加 Query Node 扛读、加 Data Node 扛写、用 replica 提吞吐和容错、用 partition key 隔离租户。这时候换库的成本远高于一开始就选一个能长大的。

项目本身的事实:Apache-2.0 许可,LF AI & Data Foundation 旗下,Zilliz 是主要贡献者,Go 与 C++ 编写,GitHub 上 46k+ stars、4.3k forks。源码在 github.com/milvus-io/milvus,文档与教程在 milvus.io。想零运维,README 也指向了 Zilliz Cloud 的全托管形态——开源与托管并行的路子,和很多基础设施项目一样。

一句话收束:Milvus 的核心不是「又一个向量库」,而是把向量检索里那些会互相拖累的东西——读与写、索引与系统、计算与存储、热与冷——逐个拆开,让每一块都能单独长大。拆得越细,能撑的规模越大,要理解的部件也越多。选它之前先想清楚:你需要的是那个规模,还是只需要一个能查 topK 的库。