Milvus 架构:基于存算分离的分布式向量数据库
Milvus 架构的核心是存算分离:proxy 和各类 worker 都是无状态的,所有 segment 和索引以对象存储为准。作为分布式向量数据库,它把向量数据、写入和检索分散到多个节点上,各自按需扩展。本文讲清分层、写入与查询流程,以及把规模推到数百亿向量的量化和磁盘索引。
- Apache-2.0,GitHub 40K+ star
- 可扩展到数百亿向量
- 对象存储是唯一数据源
- IVF_RABITQ 压缩比 1:32
- partition key 支持百万级租户
- Salesforce
- Walmart
- eBay
- NVIDIA
- Roblox
- Filevine
- Shell
Milvus 架构详解:分布式向量数据库分哪几层?
Milvus 分四层,各层独立扩展、独立容灾。新写入的流式数据由 streaming node 处理,已封存的历史数据由 query node 和 data node 处理,写入和检索不抢同一批机器,相当于天然的读写分离。
| 组件 | 层 | 职责 | 何时扩容 |
|---|---|---|---|
| Proxy(无状态) | 接入层 | 校验请求、按 shard 路由、归并结果 | 连接数或请求量上升 |
| Coordinator | 协调层 | 拓扑、DDL、时间戳、负载均衡、任务调度;全局单活 | 不横向扩展,备用实例做 HA |
| Streaming Node | Worker | 持有 shard 的 WAL、定序写入、检索 growing 数据 | 写入吞吐上升 |
| Query Node | Worker | 加载 sealed segment 和索引、执行检索 | 加载数据量或 QPS 上升 |
| Data Node | Worker | compaction 与建索引 | 建索引或 compaction 积压 |
| 对象 / WAL / 元数据存储 | 存储层 | segment 与索引;预写日志;schema 与 checkpoint | 数据量上升 |
向量数据库的存算分离是怎么实现的?
在 Milvus 里,对象存储是唯一的数据源:sealed segment、索引文件和日志快照都落在 S3、MinIO、Azure Blob 或 Google Cloud Storage 上。worker 节点无状态,新加的 query node 直接从对象存储拉 segment,扩容不需要在节点之间搬数据。
预写日志也是同样思路。从 Milvus 2.6 起,Woodpecker 把 WAL 直接写进对象存储、不依赖本地盘,取代 Kafka 或 Pulsar;Milvus 3.0 还支持把 Woodpecker 作为独立服务部署。
- 读、写、建索引分别扩容,缺哪类节点只加哪类。
- 节点宕机不丢数据,coordinator 把它的 segment 或 shard 重新分配。
- 持久数据放在便宜的对象存储上,计算层保持弹性。
- 用 resource group 隔离业务,不必拆成多个集群。
Milvus 的写入流程和查询流程是怎样的?
写入路径与检索路径在 segment 处衔接。新数据先进入 WAL 和 growing segment,何时可被某次查询看到取决于一致性级别。segment 封存并落到对象存储后,由 data 节点建索引、query 节点加载;compaction 在后台合并数据并处理删除。
- 查询流程:proxy 把请求发给每个 shard 的 streaming node,后者检索自己的 growing segment,并把 sealed segment 的子任务分发给 query node。
- 各节点返回本地 top-k,先按 shard 合并,再在 proxy 跨 shard 合并。
- 一致性级别按请求指定:Strong、Bounded(默认)、Session、Eventually。
| 阶段 | 发生了什么 | 组件 |
|---|---|---|
| 路由 | proxy 校验请求,按主键哈希到 shard(vchannel) | Proxy |
| 写日志 | 分配时间戳,追加到 WAL,写入即确认 | Streaming node → WAL 存储 |
| Growing segment | 驻留内存;可见性由一致性级别决定 | Streaming node |
| 封存与落盘 | 达到阈值后变为不可变的 sealed segment,写入对象存储 | Streaming node → 对象存储 |
| 建索引与 compaction | 构建索引;合并小 segment;落实删除 | Data node |
| 加载 | 加载 sealed segment 和索引,释放对应的 growing 数据 | Query node |
Milvus 集群扩容:读、写、建索引怎么分别扩?
写入经 proxy、streaming node 和 WAL 接收;growing 与 sealed 数据按查询的一致性级别共同提供检索。封存数据在对象存储中持久化,由 data 节点建索引、query 节点加载,各阶段可分别扩缩。
| 瓶颈 | 手段 |
|---|---|
| 写入吞吐 | 增加 shard(每个 collection 最多 16)和 streaming node |
| 建索引跟不上写入 | 增加 data node |
| 数据超出 query node 内存 | 量化或磁盘索引、mmap、分层存储,或加 query node |
| 检索 QPS | 在不同 query node 上增加内存副本 |
| 业务互相干扰 | 用 resource group 把 query node 绑定到指定 collection |
| 租户很多 | partition key:一个 collection 承载百万级租户 |
十亿级向量检索,用哪些量化索引省内存?
float32 原始向量每条占 维度 × 4 字节,HNSW 还要加图结构开销。到了十亿级,内存就是成本大头。Milvus 提供多档压缩比的向量量化,多数都支持 refine:先用压缩向量粗排,再用更高精度数据重排,把召回率补回来。
| 索引 | 向量内存占用 | 适用场景 |
|---|---|---|
| HNSW(FP32) | 完整原始向量 + 图连接 | 数据放得进内存,追求最低延迟和最高召回 |
| HNSW_SQ(SQ8、SQ6、SQ4U、FP16、BF16) | 每维 8、6、4 或 16 bit;SQ8 比 FP32 省 75% | 保留 HNSW 速度,同时压缩内存 |
| HNSW_PQ / HNSW_PRQ | PQ 压缩 4–32 倍;PRQ 加残差量化提精度 | 内存预算紧但仍想用图索引 |
| IVF_SQ8 / IVF_PQ | 每维 1 字节 / PQ 码 | 大批量、高吞吐,建索引更便宜 |
| IVF_RABITQ | 每维 1 bit,压缩比 1:32;可用 SQ6、SQ8、FP16、BF16、FP32 做 refine | 极致压缩;内存不够时替代 IVF_FLAT 或 IVF_SQ8 |
| GPU_CAGRA / GPU_IVF_PQ | 数据加载进 GPU 显存 | GPU 上的高吞吐、高召回检索 |
RaBitQ(Gao & Long,SIGMOD 2024)为 1-bit 编码给出理论误差界;在支持 AVX-512 VPOPCNTDQ 的 CPU 上更快。
数据比内存大怎么办?DiskANN 磁盘索引与冷热分层
压缩后仍放不下时,可以把索引放到本地 NVMe,或者把冷数据留在对象存储。Milvus 文档的建议是:内存只放得下约四分之一原始数据时,考虑 DiskANN。
| 方案 | 内存里放什么 | 其余数据在哪 | 适用场景 |
|---|---|---|---|
| DiskANN | PQ 码,默认为原始向量的 1/8 | Vamana 图和原始向量在 NVMe SSD | 十亿级检索,延迟稳定 |
| AISAQ(Milvus 2.6.4+) | 接近零内存;可选 PQ 缓存 | 全部索引结构在 SSD | 普通服务器上的超大规模数据;RAG、离线检索 |
| mmap | 只有热页 | 字段和索引通过内存映射放本地盘 | 数据略超内存;量化 IVF + mmap |
| 分层存储 Tiered Storage(Milvus 2.6.4+) | 元数据 + 缓存的热 chunk | 对象存储,按需拉取,冷了就淘汰 | 超出单节点内存或磁盘的 collection;关键字段可预热 |
Milvus 能扩展到多大?
Milvus 单机版(Standalone)和 Milvus 分布式部署(Distributed)的区别只在于是否把同一套组件分散到多台机器。单个 collection 的实体数不设上限,容量随 segment、shard 和节点增长。下面的默认值来自 Milvus 文档和配置。
| 项目 | 默认值 / 文档规模 |
|---|---|
| 向量规模 | 数百亿 |
| 每集群 collection 数 | 65,536(maxCollectionNum,可调) |
| 每集群 database 数 | 64 |
| 每 collection partition 数 | 1,024 |
| partition key 租户数 | 百万级,单个 collection |
| 每 collection shard 数 | 16 |
| 向量维度 | 32,768 |
延伸阅读
- How does the Milvus architecture separate ingestion, storage, and search?
- What determines vector-database scalability?
- How do sharding and replication differ in a vector database?
- What is IVF_RABITQ, and how should you evaluate it?
- When should you use DISKANN in Milvus?
- How does mmap differ from a disk-oriented ANN index?
- What is vector quantization, and what does it trade off?
- How do you turn a Milvus workload into a capacity and cost estimate?
常见问题
什么是分布式向量数据库?
把数据分片、把写入、建索引和检索分散到多个节点的向量数据库,每种能力都能靠加机器扩展。Milvus Distributed 是基于共享对象存储的 Apache-2.0 开源实现。
向量数据库为什么要做存算分离?
持久数据放在对象存储里,计算节点只做无状态的缓存和执行。加 query、streaming 或 data node 都不用搬数据,节点坏了直接替换,不用修复。
Milvus 的写入流程和查询流程是怎样的?
写入:proxy → streaming node → WAL,随后进入 growing segment 即可检索;封存后写入对象存储,data node 建索引,query node 加载。查询:proxy → streaming node(增量数据)和 query node(sealed segment)→ 合并 top-k。
Milvus 的分片和分区有什么区别?
shard 按主键哈希切分写入通道(每个 collection 最多 16 个),各由一个 streaming node 持有,决定写入并行度;partition 是按业务含义划分的数据子集(每个 collection 最多 1,024 个),查询时指定 partition 可缩小扫描范围。partition key 则让某个字段自动映射到 partition,一个 collection 承载百万级租户。
十亿级向量检索需要多少内存?
float32 原始向量每条占 维度 × 4 字节,另加索引开销。SQ8、PQ、RaBitQ 等量化,加上 DiskANN 和分层存储,都能减少必须常驻内存的部分;可用 milvus.io/tools/sizing 估算。
DiskANN 和 HNSW 有什么区别,怎么选?
原始向量放得进内存、延迟最敏感时用 HNSW。数据远超内存时用 DiskANN:图和原始向量在 NVMe SSD 上,内存里只放 PQ 码。
Milvus 有哪些一致性级别?
四种,按 search 或 query 请求指定:Strong 读最新数据;Bounded(默认)允许短暂的陈旧窗口;Session 保证同一客户端读到自己的写入;Eventually 以新鲜度换最快的读。
不想自己运维集群?
Zilliz Cloud 提供全托管 Milvus,API 与 MilvusClient 一致,节点数、升级和分层存储都由平台负责。Filevine 在上面管理着 300 亿+ 向量。
资料来源与技术文档 (32)
- https://milvus.io/
- https://milvus.io/docs/architecture_overview.md
- https://milvus.io/docs/woodpecker_architecture.md
- https://milvus.io/docs/release_notes.md
- https://milvus.io/docs/scaleout.md
- https://milvus.io/docs/resource_group.md
- https://milvus.io/docs/replica.md
- https://milvus.io/docs/multi_tenancy.md
- https://milvus.io/docs/index-explained.md
- https://milvus.io/docs/hnsw-sq.md
- https://milvus.io/docs/hnsw-pq.md
- https://milvus.io/docs/hnsw-prq.md
- https://milvus.io/docs/ivf-rabitq.md
- https://milvus.io/docs/diskann.md
- https://milvus.io/docs/aisaq.md
- https://milvus.io/docs/mmap.md
- https://milvus.io/docs/tiered-storage-overview.md
- https://milvus.io/docs/gpu-cagra.md
- https://milvus.io/docs/gpu-index-overview.md
- https://milvus.io/docs/gcs.md
- https://milvus.io/docs/limitations.md
- https://github.com/milvus-io/milvus/blob/v3.0.3/configs/milvus.yaml
- https://github.com/gaoj0017/RaBitQ
- https://zilliz.com/customers/meituan
- https://zilliz.com/customers/kling-ai
- https://zilliz.com/customers/filevine
- https://github.com/milvus-io/milvus
- https://github.com/milvus-io/milvus-docs/tree/v3.0.x
- https://milvus.io/docs/consistency.md
- https://milvus.io/docs/glossary.md
- https://milvus.io/docs/zh/architecture_overview.md
- https://milvus.io/docs/install-overview.md