第 1 篇 给出了 Milvus 四层坐标系。排障与读源码时,真正反复出现的不是「向量库」三个字,而是一组更硬的名词:Collection、Partition、Segment、Channel(vchannel / pchannel),以及 Segment 在 Growing 与 Sealed 之间的迁移。
本文只建立数据模型与状态机,不展开 WAL 实现细节(第 5 篇)也不展开 Knowhere 索引类型(第 8 篇)。读完应能回答:一条 insert 先落在哪种 Segment 上?何时变成不可变?谁负责把它交给 Query Node?
本文是「向量检索引擎」系列第 3 篇(共 18 篇)。→ 系列目录
版本锚定:Milvus 2.6.x Data Processing、Architecture Overview、Streaming Service。
一、Collection 与 Partition:逻辑容器
Collection
是用户可见的向量(及标量)表:有
schema(主键、向量字段、标量字段等),是 insert
/ search 的默认作用域。官方架构与数据流都以
Collection 为顶层逻辑对象。
Partition 是 Collection 内的逻辑分区,用于按业务键隔离数据(例如按租户、按时间桶)。分区影响数据落在哪些 channel / segment 上的组织方式,但 不改变 Growing / Sealed 的段语义——后文状态机对每个 segment 成立。
本系列不把 schema 演进、动态字段的每一种 API 写成手册;需要时以官方 User Guide 为准。内核视角记住一点即可:检索与写入的调度单元落到 Channel 与 Segment,而不是「整个 Collection 一个大数组」。
二、Channel:shard 与 Streaming Node 的绑定
官方 Data Processing 写明:
- Collection 可配置多个 shard;每个 shard 对应一个 虚拟通道(vchannel)。
- 每个 vchannel 映射到一个 物理通道(pchannel)。
- 每个 pchannel 绑定到特定 Streaming Node。
Proxy 在写入时按 shard 路由规则拆包,把同一 vchannel 的数据送到对应 Streaming Node。于是:
- 水平扩展写入:增加 shard / Streaming Node,而不是把所有 insert 打进单日志。
- 顺序与一致性作用域:Streaming Service 文档强调,Message 属于某个 VChannel,并在该 channel 内维持操作顺序不变量(例如同一 channel 上 Insert 相对 DropCollection 的先后)。
flowchart LR
coll["Collection"]
v1["vchannel shard0"]
v2["vchannel shard1"]
p1["pchannel"]
p2["pchannel"]
sn1["Streaming Node A"]
sn2["Streaming Node B"]
coll --> v1
coll --> v2
v1 --> p1 --> sn1
v2 --> p2 --> sn2
与第 5 篇的衔接:WAL 不是「全集群一个文件」,而是 多个可独立扩展的日志;任一时刻,一个 WAL 组件只允许在一个 Streaming Node 上服务(fencing + Streaming Coordinator 保证)。本文先记住绑定关系,WAL 形态留给第 5 篇。
三、Segment:Growing 与 Sealed
Streaming Node 把已提交的 WAL 条目切成离散的 Segment。官方明确定义两类:
| 类型 | 定义(官方 Data Processing) | 可变性 | 谁主要服务查询 |
|---|---|---|---|
| Growing | 尚未持久化到对象存储的数据 | 可变、持续追加 | Streaming Node 本地维护并参与实时查询 |
| Sealed | 已全部持久化到对象存储 | 不可变 | Query Node 从对象存储加载后查询 |
这是整条系列最重要的二分:
- 实时性依赖 Growing:WAL 提交后即可被 Growing 路径搜到(Architecture 插入流)。
- 大规模历史检索与向量索引依赖 Sealed:索引构建在 Data Node 上对 Sealed 进行,输入输出都走对象存储(Data Processing · Index building)。
3.1 Flush:Growing → Sealed
Growing 变为 Sealed 的过程叫 flush。官方描述触发时机:Streaming Node 在该 segment 上 已摄入并写完所有可用 WAL 条目、底层 WAL 无更多 pending 记录 时,将该 segment finalize,使之面向只读优化。
Streaming Service 还提到 WAL 组件按策略管理 segment 生命周期,例如内存条件、segment 大小、空闲时间等——具体阈值是运维参数,本篇只保留 策略驱动的生命周期,不伪造默认阈值数字。
3.2 Handoff:历史数据交给 Query Node
当 Growing 被 flush 成 Sealed,或 Data Node 完成 compaction 后,Coordinator 发起 handoff:把该段从「流式侧持有的增长数据」转为「历史数据」,并在 Query Node 之间分配 Sealed,平衡内存、CPU 与 segment 数量,释放冗余副本(Data Processing · Handoff)。
stateDiagram-v2
[*] --> Growing: WAL commit append
Growing --> Sealed: flush
Sealed --> Indexed: DataNode index build
Sealed --> Compacted: DataNode compaction
Indexed --> Serving: handoff to QueryNode
Compacted --> Serving: handoff to QueryNode
「Indexed」在此是叙述辅助态:官方强调 每个 segment 可有自己的索引,建索由 Data Node 完成;Query Node 加载新建索引并替换对应的增长数据视图(Architecture 插入流第 7 步)。排障时「有数据但召回差」经常落在 Sealed 已可见但索引未完成 / 未 handoff——第 10、17 篇展开。
四、一次写入在模型上的落点
把官方插入流压成模型层步骤(不含 SDK 细节):
- 客户端
insert→ Proxy 校验。 - 按 shard 规则拆到若干 vchannel 数据包。
- 对应 Streaming Node 赋 TSO,做一致性检查,写入 WAL;提交后不丢(崩溃可 replay)。
- 异步切分 / 追加到 Growing Segment;此时可被增量查询路径看到。
- 策略满足 → flush → Sealed 落对象存储。
- Data Node 建索引 / compaction → Coordinator handoff → Query Node 加载。
TSO 提供操作全序;同一 VChannel 内的 Message 顺序决定该 shard 上写操作的可见顺序(Streaming Service)。一致性级别(Strong / Bounded / …)如何裁剪可见集合,见第 12 篇;本篇只要求承认:顺序首先是 channel 局部的。
五、一次查询在模型上的落点
官方查询流:
- Collection 拆成多 segment;Streaming Node 持 Growing,Query Node 持 Sealed。
- Proxy 向相关 shard 的 Streaming Node 并发请求。
- 每个 Streaming Node:搜本地 Growing;联系 Query Node 取历史(Sealed)结果;聚合成 shard 结果。
- Proxy 合并各 shard 结果返回。
因此「Collection 上的一次 search」在引擎里是 多 Growing + 多 Sealed 的候选归并,不是单次 FAISS 调用。Segcore / Knowhere 在段内做什么,见第 7–8 篇;跨段 reduce 见第 9 篇。
六、学术谱系与工程间隙
6.1 谱系:动态向量数据 vs 静态 ANN 基准
| 阶段 | 工作 / 系统 | 假设 |
|---|---|---|
| ANN 基准 | ann-benchmarks、Big-ANN 等 | 多以 静态集 建索引再查询 |
| 向量数据管理系统 | Wang et al., SIGMOD 2021 | 强调 动态更新 与查询并存 |
| Milvus 2.6.x | Growing / Sealed + handoff | 用段状态机落实「可变增量 + 不可变历史」 |
Growing/Sealed 二分与列存的「可变缓冲 + 不可变 part」、LSM 的「MemTable + SST」是 同一类工程分形:前台写进可变结构,后台固化为只读、可建重索引的单元。差异在于固化单元上挂的是 高维 ANN 索引,而不是 B-Tree 页或列块(对照 columnar-engine、rocksdb 的 part/SST 直觉,勿混实现)。
6.2 工程间隙
- 论文或博客常画「一个 Collection 一个 HNSW」;生产是 每 Sealed segment 一份索引,查询必须归并。
- flush / handoff 延迟窗口内,数据可能「WAL 已提交、索引未就绪」——可见性与召回质量不是同一时刻切换。
- 对象存储上的 Sealed 不可变,但 compaction 会用新 Sealed 替换旧布局;Query Node 视图由 Coordinator 调度,不是「文件一旦写出永不迁移」。
6.3 开放问题
- 最优 segment 大小:太大则 flush/建索毛刺明显,太小则 segment 数爆炸、归并与元数据成本上升——依赖负载,缺普遍解析解。
- 多向量字段 / 多索引 时,segment 内多份索引的生命周期如何与单次 handoff 对齐(第 10、18 篇)。
- 与湖表 snapshot 的跨系统对齐:同一 embedding 版本在 Iceberg 与 Milvus 之间如何证明一致(lakehouse/21 边界)。
七、小结
记住四组词即可驱动后续篇章:
- Collection / Partition — 逻辑容器与隔离。
- vchannel → pchannel → Streaming Node — 写入与增量查询的 shard 绑定。
- Growing / Sealed — 可变实时 vs 不可变历史。
- flush / index build / handoff — 从实时路径进入历史服务路径的迁移。
下一篇可先读写路径深化:Proxy 与 Coordinator,或 Streaming Node 与 Woodpecker WAL。
参考资料
- Milvus Documentation v2.6.x, Data Processing(insertion、Growing/Sealed、flush、index building、query、handoff)。
- Milvus Documentation v2.6.x, Architecture Overview(四层、insert/search 示例流)。
- Milvus Documentation v2.6.x, Streaming Service(VChannel、Message、WAL 组件与 segment 生命周期策略)。
- Wang et al., Milvus: A Purpose-Built Vector Data Management System, SIGMOD 2021(动态向量数据管理系统动机)。
- 第 1 篇 全景、系列 index。
返回 系列目录 | 上一篇:向量引擎全景 | 下一篇:Proxy 与 Coordinator
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【向量检索引擎】Query Node 与 Segcore:段级 search 如何执行
按 Milvus 2.6.x Data Processing 与 Architecture 拆解 Query Node 对 Sealed 的加载与段级检索,说明 Streaming Node 上 Growing 路径与 Query Delegator 如何拼成一次 search,并界定 Segcore 与 Knowhere 的层次。
【向量检索引擎】Data Node:compaction 与 index build
说明 Milvus 2.6.x 中 Data Node 作为离线 Worker 如何承接 Coordinator 下发的建索引与 compaction,输入输出如何进出对象存储,以及索引堆积与 handoff 如何影响查询新鲜度与资源争用。
【向量检索引擎】对象存储上的 Segment 布局:快照、索引与寻址代价
说明 Milvus 2.6.x 对象存储持久化的职责边界(日志快照、标量/向量索引、中间结果),Growing flush 后 Sealed 如何进共享存储,以及按字段拆分 binlog 与按段整合(StorageV2/Parquet)在对象存储 API 次数上的工程含义。
【向量检索引擎】副本、负载与故障恢复:内存副本与 WAL 迁移
按官方 In-Memory Replica 与 Streaming Service 说明副本组、shard/historical 副本、Proxy 缓存与 failover;对照 2.6 Streaming Delegator 多副本,以及 WAL Wait for Ready 与 handoff 负载均衡。