Milvus 架构深挖版

目标:把 Milvus 当前 master 分支里的真实架构拆开看清楚,尤其是控制面、数据面、WAL/streaming、存储和查询执行之间的关系。


1. 为什么说 Milvus 是“数据库系统”而不是“ANN 封装”

Milvus 真正解决的问题不是“给你一个 HNSW 接口”,而是这组系统问题:

  • 如何持续接收 mutation
  • 如何在近实时写入下保持可查
  • 如何把新数据转成适合分布式加载和索引管理的物理块
  • 如何做 segment 的放置、加载、均衡、恢复和重构
  • 如何用统一的时间语义管理写入可见性

因此,如果用数据库术语去理解 Milvus,它的关键抽象是:

  • catalog / metadata
  • mutation log / WAL
  • segment
  • background maintenance
  • query scheduling
  • execution engine

2. 当前架构中的控制面:MixCoord 统一编排

2.1 MixCoord 是控制面容器

internal/coordinator/mix_coord.go 中,mixCoordImpl 直接持有:

  • rootcoordServer
  • queryCoordServer
  • datacoordServer
  • streamingCoord

这意味着当前控制面并不是“独立 RootCoord / DataCoord / QueryCoord 三个完全分离的 server 进程”这种最朴素形态,而是进程级聚合 + 模块级分层

2.2 RootCoord:全局 metadata 与时序入口

RootCoord 的本质是系统级元数据主控。它负责:

  • collection / partition / alias / schema DDL
  • 全局 ID / timestamp 分配
  • 某些 cache invalidation / service coordination
  • 和 streaming / proxy / datacoord 等模块协同更新系统状态

如果类比数据库内核,它更接近 catalog manager + global timestamp service。

2.3 DataCoord:segment 生命周期控制器

DataCoord 是写路径里最核心的控制面模块之一。它关心的是:

  • segment 什么时候是 growing / sealed / flushed
  • 哪些 segment 该触发 index build
  • 哪些 segment 需要 compaction
  • flush checkpoint 和持久化状态如何维护

Milvus 的“数据库性”很大程度体现在 DataCoord 上:它把 mutation 和物理 segment 的管理做成了一个完整 subsystem。

2.4 QueryCoordV2:查询面资源调度器

QueryCoordV2 负责:

  • load / release collection
  • replica 管理
  • channel / segment 分发
  • 节点负载均衡
  • 资源组与恢复

为什么它很重要?因为在分布式检索系统里,真正难的往往不是向量距离计算,而是:

  • 数据该放在哪里
  • 怎么在节点增减和失败时恢复
  • 如何保障热点 collection 的吞吐与副本均衡

2.5 StreamingCoord:WAL 子系统控制面

StreamingCoord 是当前架构演进里非常关键的新角色。它负责:

  • PChannel 到 StreamingNode 的分配
  • 通道级健康管理
  • DDL/DCL 的跨通道广播协调
  • streaming service discovery 与控制面事务

它不是“边缘模块”,而是当前 mutation 事实源模型里的关键控制层。


3. 数据面:真正处理写入和查询的节点

3.1 Proxy:请求编排层

internal/proxy/task_insert.gotask_search.go 展示了 Proxy 的真正角色:

插入前处理包括:

  • collection / schema 拉取
  • request size 和合法性检查
  • rowID 自动分配
  • timestamp 填充
  • dynamic field / namespace / struct field 处理
  • schema timestamp / version 检查

查询前处理包括:

  • collection schema 绑定
  • partition / partition key 模式处理
  • output fields 翻译
  • guarantee timestamp / consistency level 推导
  • advanced search / trace / ignore growing 等参数初始化

因此 Proxy 不是 dumb gateway,而是 用户请求语义转换层

3.2 DataNode:mutation → segment buffer → flush

DataNode 负责:

  • 消费 WAL/mq 中的 mutation
  • 在内存中组织 growing segment
  • 把缓冲数据转换成可 flush 的物理形式
  • 生成 binlog / deltalog / statslog
  • 上传对象存储

关键实现之一是 internal/flushcommon/writebuffer/write_buffer.go

WriteBuffer 接口可以看到它支持:

  • CreateNewGrowingSegment
  • BufferData
  • SealSegments
  • GetCheckpoint
  • EvictBuffer
  • SetFlushTimestamp

这说明 DataNode 内部有非常典型的“按 channel 组织的 mutable segment / flush buffer”结构。

3.3 QueryNodeV2:真正的查询执行节点

QueryNodeV2 一方面要加载 sealed segment 和已建好的索引,另一方面要把 growing segment 中尚未固化的数据纳入查询可见性。

其最核心抽象是 ShardDelegator

  • 维护 shard/vchannel 级状态
  • 统一处理 sealed / growing / delete / tsafe
  • 协调本地和远端查询执行
  • 同步 partition stats 和 segment 分布

可以把它理解为 QueryNode 上的 per-shard runtime。它是 Milvus 查询系统最复杂、也最值钱的地方之一。

3.4 StreamingNode:WAL/PChannel 承载体

StreamingNode 负责:

  • 管理一部分 PChannels
  • 提供 append / read / tx / timetick 等 WAL 服务
  • 参与恢复和 broadcaster ACK 路径

这一层使 mutation log 在系统里具备明确的“node identity”和“分片归属”。


4. WAL / MQ / Streaming:当前系统的事实源设计

4.1 配置层表明消息后端是可插拔的

configs/milvus.yaml 明确支持:

  • rocksmq
  • pulsar
  • kafka
  • woodpecker

并且对 standalone / cluster 模式给出不同默认优先级。说明 Milvus 的日志后端不是死板绑定的,而是抽象了一层统一 WAL / MQ 语义。

4.2 新 streaming 文档的中心思想

docs/agent_guides/streaming-system/streaming-system.md 的最关键一句话是:

Milvus uses a log-structured WAL as its single source of truth for all data mutations and metadata changes.

这件事意义很大。它表示:

  • mutation 和一部分 metadata 变化都进入统一日志语义
  • 其他系统状态可以被视为该日志的物化形式或恢复结果

4.3 PChannel / VChannel / CChannel 三层模型

  • PChannel:物理分片,映射到 StreamingNode 和 backend topic
  • VChannel:逻辑 shard,通常按 collection 的分片维度组织
  • CChannel:控制通道,用于 cluster-wide ordering / control event

这使得:

  • DML 可以按 append 模式进入具体物理通道
  • DDL/DCL 可通过广播器跨多个 PChannel 原子提交
  • streaming system 能同时表达物理分布和逻辑 collection shard

4.4 DML / DDL 的不同路径

文档中的数据流很清楚:

  • DML:Client → Proxy → StreamingClient.Append → StreamingNode → WAL backend
  • DDL/DCL:Client → Proxy → StreamingClient.Broadcast → StreamingCoord.Broadcaster → StreamingNodes → WAL backend

这背后的系统思想是:

  • 普通数据 mutation 追求吞吐和顺序 append
  • 控制类变更追求跨通道原子性和有序性

5. segment 生命周期:Milvus 的主轴

5.1 segment 是统一物理单元

无论是写入、索引、查询、调度还是 compaction,Milvus 几乎都以 segment 为基本物理单元。

5.2 生命周期大致可以理解为

  1. 创建 growing segment
  2. 接收 mutation
  3. sealed
  4. flush 到对象存储
  5. index build
  6. load 到 QueryNode
  7. 被 compaction / clustering / sort 重写

这个设计让 Milvus 具备两个关键能力:

  • 近实时:growing segment 立即参与查询
  • 高性能:sealed/indexed segment 提供高效 ANN 检索

5.3 为什么这个设计成立

如果所有数据都必须等索引建好才可查,Milvus 就失去实时性;如果所有数据都永远只在内存 growing 态里查,就失去成本和吞吐优势。Milvus 通过 dual-path:

  • growing path
  • sealed/indexed path

把 freshness 和 query efficiency 分开处理。


6. 存储层:metadata、对象存储、本地缓存

6.1 metadata:etcd / TiKV

配置里可以看到:

  • etcd 用于 metadata 和 service discovery
  • metastore.type 可切换到 tikv

这意味着:

  • etcd 仍是服务协调核心
  • TiKV 被用来提升 metadata 扩展能力

6.2 对象存储:MinIO/S3 是持久化承载体

Milvus 把:

  • binlog
  • deltalog
  • statslog
  • index files

都落在对象存储上。这是计算存储分离的关键。

相关实现位于:

  • internal/storage/
  • internal/core/src/storage/

6.3 localStorage:查询节点本地化缓冲

配置里的 localStorage.path 用于在 search/query 时减少对 MinIO/S3 的重复访问。说明 QueryNode 不会把对象存储当作低延迟在线存储,而是会把必要数据 materialize 到本地。


7. 查询架构深挖:为什么 QueryNodeV2 才是精华

7.1 QueryCoord 负责 placement

QueryCoordV2 决定:

  • 哪些 segment / channel 去哪个节点
  • 哪些 replica 服务这个 collection
  • 扩缩容和节点故障后怎么恢复

7.2 QueryNodeV2 负责 execution state

真正复杂的状态组织在 QueryNodeV2。特别是 ShardDelegator 内部维护:

  • sealed segment 分布
  • growing segment
  • delete buffer
  • partition stats
  • schema version
  • catchingUpStreamingData
  • latestRequiredMVCCTimeTick

这说明查询执行在 Milvus 里不是一个“直接查索引”的简单过程,而是一个 持续维护可见性、增量、负载和分布状态 的系统。

7.3 C++ segcore 是最终执行点

internal/core/src/segcore/segment_c.h 暴露的 C 接口包括:

  • NewSegment
  • SegmentLoad
  • AsyncSegmentLoad
  • AsyncSearch
  • AsyncRetrieve
  • Insert
  • GetMemoryUsageInBytes

这几乎已经直接说明架构边界:

  • Go 层 orchestrate
  • C++ 层 execute

因此查询分析不能只停留在 Go 层,否则会错过真正的数据访问和检索内核。


8. 后台任务体系:compaction 不再是附属功能

internal/datacoord/compaction_trigger_v2.go 说明当前 Milvus 的 compaction 已经形成了一套策略系统,而不只是“小文件合并”。

当前触发类型包括:

  • L0 delete compaction
  • mix compaction
  • clustering compaction
  • sort compaction
  • force merge
  • storage version upgrade
  • backfill compaction

并且有多个 ticker 周期性巡检。

这说明 Milvus 背后的长期维护逻辑已经在向成熟数据库系统靠近:

  • 数据整理
  • 删除清理
  • 排序/聚类优化
  • 版本升级重写
  • 后台回填

9. Milvus 当前最值得研究的三个系统问题

问题 1:如何让 mutation log 真正成为系统事实源

Streaming 体系是这个答案的一部分。

问题 2:如何让 segment 同时服务写入、查询、索引和 compaction

segment lifecycle 是这个答案的核心。

问题 3:如何在近实时状态下做正确的分布式查询

QueryNodeV2 + QueryCoordV2 + ShardDelegator 是这个答案的核心。


10. 最后的压缩理解

如果要把当前 Milvus 压缩成一句工程判断:

Milvus 是一个以 WAL 驱动 mutation、以 segment 为物理核心、以对象存储承载持久化、以 QueryNodeV2/segcore 提供执行能力、以 MixCoord 聚合控制面的分布式 AI 检索数据库。