Milvus 总览与重新总结
摘要
Milvus 当前可以理解为一个围绕 segment 生命周期 组织起来的分布式检索数据库,而不是单纯的 ANN 索引封装。它通过 控制面 / 数据面分离、WAL 或消息流作为 mutation 事实源、对象存储作为持久化载体、C++ segcore 作为执行内核,把向量检索、标量过滤、增量写入、异步索引构建、后台 compaction 和分布式查询调度整合在同一个系统里。
本总结基于本地源码仓库 /home/zouhaipeng/work/db/milvus(提交 fe13e4cb06)以及官方文档和 GitHub 材料整理。
一句话结论
Milvus 的核心不是某种 ANN 算法,而是:如何把 mutation 变成 segment,如何围绕 segment 做 flush、索引、加载、查询、compaction 和调度。
当前架构的主判断
1. 当前代码已进入 MixCoord + Streaming 时代
旧文档例如 docs/developer_guides/chap01_system_overview.md 更像 Milvus 早期的总体设计抽象,强调 Proxy / WAL hash ring / query node / write node 这些角色关系。它仍然有参考价值,但已经不能完整代表当前 master 的真实进程组织方式。
当前源码更重要的事实是:
cmd/main.go默认开启 streaming serviceinternal/coordinator/mix_coord.go把 RootCoord、DataCoord、QueryCoordV2、StreamingCoord 聚合在同一个 MixCoord 实现中internal/querynodev2/和internal/querycoordv2/已经成为查询面的主要实现docs/agent_guides/streaming-system/streaming-system.md明确把 WAL 描述成 single source of truth
2. Milvus 是 Go 控制面 + Go 数据面编排 + C++ 执行内核
可以把系统分成四层:
- 入口层:
cmd/ - 控制面与分布式服务层:
internal/rootcoord、internal/datacoord、internal/querycoordv2、internal/proxy、internal/datanode、internal/querynodev2、internal/streamingcoord、internal/streamingnode - 公共基础设施层:
pkg/ - 执行内核层:
internal/core/src/
其中:
- Go 层负责 RPC、调度、metadata、生命周期、后台任务、配置和可观测性
- C++ 层负责 segment 内真正的数据装载、搜索、retrieve、索引和存储访问
3. segment 是整个系统的统一物理单元
Milvus 中:
- collection 是逻辑表
- partition 是逻辑分区
- segment 是最核心的物理组织单元
写入、flush、load、index build、query serving、compaction、replica placement,几乎都围绕 segment 展开。
组件分工:当前实现最该记住的图
控制面
- MixCoord:当前控制面聚合点
- RootCoord:DDL、全局 metadata、ID/TS 分配
- DataCoord:segment 生命周期、flush 状态、index task、compaction
- QueryCoordV2:load/release、replica、segment/channel 分配和均衡
- StreamingCoord:PChannel 分配、广播协调、streaming 控制面
数据面
- Proxy:用户入口,请求规范化、schema 绑定、参数校验、编排
- DataNode:消费 mutation、组织 growing segment、flush 到对象存储
- QueryNodeV2:执行搜索 / 查询,维护 sealed/growing 数据可见性
- StreamingNode:承载 WAL/PChannel 的实际读写与恢复
外部依赖
- etcd / TiKV:metadata 与服务协调
- MinIO / S3:binlog、索引文件、持久化对象
- Kafka / Pulsar / Rocksmq / Woodpecker:WAL 或消息后端
执行内核
internal/core/src/segcoreinternal/core/src/indexinternal/core/src/queryinternal/core/src/storage
三条最关键主线
主线 1:segment 生命周期
建议用这条链去理解系统:
- Proxy 接受写请求
- mutation 写入 WAL / stream
- DataNode 消费并形成 growing segment
- growing segment 达到条件后 seal / flush
- flush 产出 binlog / deltalog / statslog,写入对象存储
- DataCoord 更新 segment metadata
- 后台异步构建 index
- QueryCoordV2 将 segment 分配到 QueryNodeV2
- QueryNodeV2 加载并提供查询
- 后台 compaction 持续重写 segment 组织
主线 2:WAL / Streaming 是 mutation 事实源
在 docs/agent_guides/streaming-system/streaming-system.md 中,Milvus 已明确把 WAL 视为 single source of truth。当前 streaming 架构中的关键抽象包括:
- PChannel:物理通道
- VChannel:逻辑通道
- CChannel:控制通道
- StreamingCoord:通道分配与广播协调
- StreamingNode:WAL/PChannel 的实际承载节点
- StreamingClient:各组件访问 streaming/WAL 的统一入口
这让 Milvus 更像“数据库 + 分布式日志子系统”的组合,而不只是“数据库依赖一个消息队列”。
主线 3:QueryNodeV2 的真正核心是 ShardDelegator
internal/querynodev2/delegator/delegator.go 是最值得精读的文件之一。它维护:
- sealed / growing segment 分布
- delete buffer
- partition stats
- schema version
- streaming catch-up 状态
- tsafe / mvcc 时间推进
换句话说,QueryNodeV2 真正的难点不是“调用索引做搜索”,而是 如何在一个 shard 上统一处理 sealed 数据、growing 数据、delete、生效时间和远端分发。
你应该怎么理解 Milvus
不要把它理解成“ANN 库 + 一层服务”
Milvus 解决的是数据库级问题:
- 元数据
- 增量写入
- 一致性可见性
- segment 生命周期
- 后台整理
- 复制和调度
- 多租户和资源隔离
更像什么
- 一个面向向量 / 混合检索场景的分布式数据库
- 一个以 segment 为物理核心的数据组织系统
- 一个用 Go 写 control plane、用 C++ 写 execution engine 的云原生检索平台
当前演进方向是什么
从 README、官方 docs 和源码都能看出来,Milvus 正在往“AI Search Database”演进,而不只是 vector DB。现在它已经把:
- dense vector
- sparse vector
- full-text / BM25
- hybrid search
- metadata filtering
- 流式更新
逐步收进统一系统语义中。
读源码时最应该盯住的 10 个文件 / 目录
cmd/main.gointernal/types/types.gointernal/coordinator/mix_coord.gointernal/proxy/task_insert.gointernal/proxy/task_search.gointernal/flushcommon/writebuffer/write_buffer.gointernal/querycoordv2/internal/querynodev2/delegator/delegator.gointernal/datacoord/compaction_trigger_v2.gointernal/core/src/segcore/segment_c.h
最终总评
Milvus 当前最有研究价值的地方在于:
- 它把向量数据库问题真正做成了数据库系统问题
- 它的核心抽象不是 ANN index,而是 segment lifecycle
- 它的系统复杂度主要来自状态协调,而不是搜索算法本身
如果从存储和数据库内核视角学 Milvus,我建议重点把握三件事:
- WAL / streaming 如何作为 mutation source of truth
- segment 如何贯穿 write → flush → index → query → compaction
- QueryNodeV2 / ShardDelegator 如何把近实时与离线索引态统一起来