土法炼钢兴趣小组的算法知识备份

【向量检索引擎】Collection · Partition · Segment · Channel:Growing 到 Sealed 的状态机

文章导航

分类入口
databasestorage
标签入口
#milvus#segment#collection#channel#growing#sealed#flush#handoff#vector-engine

源码下载

本文相关源码已整理,共 1 个文件。

打开下载目录 →

目录

第 1 篇 给出了 Milvus 四层坐标系。排障与读源码时,真正反复出现的不是「向量库」三个字,而是一组更硬的名词:Collection、Partition、Segment、Channel(vchannel / pchannel),以及 Segment 在 GrowingSealed 之间的迁移。

本文只建立数据模型与状态机,不展开 WAL 实现细节(第 5 篇)也不展开 Knowhere 索引类型(第 8 篇)。读完应能回答:一条 insert 先落在哪种 Segment 上?何时变成不可变?谁负责把它交给 Query Node?

本文是「向量检索引擎」系列第 3 篇(共 19 篇)。→ 系列目录

版本锚定:Milvus v2.6.21 Data ProcessingArchitecture OverviewStreaming Service


一、最小故事:一条 insert 落在哪

客户端对 Collection docs 插入一批向量。引擎不会把它们塞进「一个名为 docs 的大数组」,而是:

  1. 按 shard 规则把行分到某个 vchannel
  2. vchannel 映射到 pchannel,绑定到一台 Streaming Node
  3. 该节点赋 TSO、写 WAL,追加进当前 Growing Segment
  4. 策略触发后 Growing flushSealed,落对象存储;
  5. Data Node 建索引后,Coordinator handoff,由 Query Node 加载历史段。

记住这条故事,后面所有名词都是在给它命名。若排障时只盯着 Collection 名,而忽略 Channel / Segment,就会在「数据在不在」「索引好了没」「该问哪台节点」上反复迷路。


二、Collection 与 Partition:逻辑容器

Collection 是用户可见的向量(及标量)表:有 schema(主键、向量字段、标量字段等),是 insert / search 的默认作用域。官方架构与数据流都以 Collection 为顶层逻辑对象。

Partition 是 Collection 内的逻辑分区,用于按业务键隔离数据(例如按租户、按时间桶)。分区影响数据落在哪些 channel / segment 上的组织方式,但 不改变 Growing / Sealed 的段语义——后文状态机对每个 segment 成立。

flowchart TB
  coll["Collection"]
  p0["Partition A"]
  p1["Partition B"]
  s0["Segments ..."]
  s1["Segments ..."]
  coll --> p0 --> s0
  coll --> p1 --> s1

本系列不把 schema 演进、动态字段的每一种 API 写成手册;需要时以官方 User Guide 为准。内核视角记住一点即可:检索与写入的调度单元落到 Channel 与 Segment,而不是「整个 Collection 一个大数组」。


三、Channel:shard 与 Streaming Node 的绑定

官方 Data Processing 写明:

  1. Collection 可配置多个 shard;每个 shard 对应一个 虚拟通道(vchannel)
  2. 每个 vchannel 映射到一个 物理通道(pchannel)
  3. 每个 pchannel 绑定到特定 Streaming Node

Proxy 在写入时按 shard 路由规则拆包,把同一 vchannel 的数据送到对应 Streaming Node。于是:

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 从对象存储加载后查询

这是整条系列最重要的二分:

4.1 Flush:Growing → Sealed

Growing 变为 Sealed 的过程叫 flush。官方描述触发时机:Streaming Node 在该 segment 上 已摄入并写完所有可用 WAL 条目、底层 WAL 无更多 pending 记录 时,将该 segment finalize,使之面向只读优化。

Streaming Service 还提到 WAL 组件按策略管理 segment 生命周期,例如内存条件、segment 大小、空闲时间等——具体阈值是运维参数,本篇只保留 策略驱动的生命周期,不伪造默认阈值数字。

4.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 篇展开。

4.2.1 源码落点:Growing 状态在元数据里(milvus v2.6.21

DataCoord 侧用 datapb.SegmentInfo 包装段元数据;构造 Growing 段时显式按 commonpb.SegmentState_Growing 初始化分配与 flush 时钟:

// milvus-io/milvus v2.6.21
// internal/datacoord/segment_info.go
func NewSegmentInfo(info *datapb.SegmentInfo) *SegmentInfo {
    s := &SegmentInfo{SegmentInfo: info}
    if s.GetState() == commonpb.SegmentState_Growing {
        s.allocations = make([]*Allocation, 0, 16)
        s.lastFlushTime = time.Now().Add(-1 * paramtable.Get().DataCoordCfg.
            SegmentFlushInterval.GetAsDuration(time.Second))
        s.lastWrittenTime = getZeroTime()
    }
    s.deltaRowcount.Store(-1)
    return s
}

这说明「Growing / Sealed」不是文档修辞,而是 元数据状态机上的枚举;flush 后状态迁到 flushed/sealed 一类只读态,再进入建索与 handoff(具体枚举名以该 tag 的 commonpb.SegmentState 为准)。二次索引 coll2Segments / channel2Segments 也在同文件,对应「调度单元是 Channel + Segment」。

4.3 flush 与 handoff 时序(教学)

sequenceDiagram
  participant SN as StreamingNode
  participant OS as ObjectStore
  participant DN as DataNode
  participant Coord as Coordinator
  participant QN as QueryNode
  SN->>SN: Growing append
  SN->>OS: flush Sealed binlog
  Coord->>DN: schedule index / compact
  DN->>OS: write index files
  Coord->>QN: handoff
  QN->>OS: load Sealed + index

五、一次写入在模型上的落点

把官方插入流压成模型层步骤(不含 SDK 细节):

  1. 客户端 insert → Proxy 校验。
  2. 按 shard 规则拆到若干 vchannel 数据包。
  3. 对应 Streaming Node 赋 TSO,做一致性检查,写入 WAL;提交后不丢(崩溃可 replay)。
  4. 异步切分 / 追加到 Growing Segment;此时可被增量查询路径看到。
  5. 策略满足 → flushSealed 落对象存储。
  6. Data Node 建索引 / compaction → Coordinator handoff → Query Node 加载。

TSO 提供操作全序;同一 VChannel 内的 Message 顺序决定该 shard 上写操作的可见顺序(Streaming Service)。一致性级别(Strong / Bounded / …)如何裁剪可见集合,见第 12 篇;本篇只要求承认:顺序首先是 channel 局部的。


六、一次查询在模型上的落点

官方查询流:

  1. Collection 拆成多 segment;Streaming Node 持 Growing,Query Node 持 Sealed。
  2. Proxy 向相关 shard 的 Streaming Node 并发请求。
  3. 每个 Streaming Node:搜本地 Growing;联系 Query Node 取历史(Sealed)结果;聚合成 shard 结果
  4. Proxy 合并各 shard 结果返回。

因此「Collection 上的一次 search」在引擎里是 多 Growing + 多 Sealed 的候选归并,不是单次 FAISS 调用。Segcore / Knowhere 在段内做什么,见第 7–8 篇;跨段 reduce 见第 9 篇。

flowchart TB
  proxy["Proxy"]
  sn["Streaming Node Delegator"]
  grow["Growing search"]
  qn["Query Node Sealed search"]
  proxy --> sn
  sn --> grow
  sn --> qn
  sn -->|"shard reduce"| proxy

七、教学数值:可见性与索引质量不是同一时刻

下面用 教学示意(非实测)帮助记忆时间窗。设 \(t=0\) 时 insert 的 WAL 提交成功:

时刻(示意) 客户端可感知 引擎内部
\(t=0\) insert 返回成功 Message 在 WAL;Growing 可搜
\(t=t_f\) 仍可搜到(Growing 或刚 Sealed) flush 完成,对象存储有 binlog
\(t=t_i\) 召回形态可能变化 Data Node 写出 index files
\(t=t_h\) 历史路径由 Query Node 服务 handoff 完成,增长视图被替换

\(t_f\)\(t_i\)\(t_h\) 的相对顺序与间隔依赖策略与队列深度(第 5、10、17 篇)。一致性级别(第 12 篇)裁剪的是「读请求保证看到哪个时间戳之前的写」,不等于「索引已经建好」。


八、常见误解

  1. 「一个 Collection 对应一张全局 HNSW」。生产是 每 Sealed 一份索引;查询必须归并多段候选。
  2. 「insert 返回成功 = 对象存储上已有 Sealed + 索引」。返回成功通常意味着 WAL 路径可提交;flush / 建索 / handoff 是后续异步阶段。
  3. 「Partition 改变了 Growing/Sealed 语义」。Partition 是逻辑隔离;段状态机对每个 segment 相同。
  4. 「shard 数等于 Query Node 数」。shard(vchannel)绑定的是 Streaming / WAL 所有权;Query Node 按 segment 负载分配 Sealed,二者相关但不相等。

九、学术谱系与工程间隙

9.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-enginerocksdb 的 part/SST 直觉,勿混实现)。

9.2 工程间隙

9.3 开放问题

  1. 最优 segment 大小:太大则 flush/建索毛刺明显,太小则 segment 数爆炸、归并与元数据成本上升——依赖负载,缺普遍解析解。
  2. 多向量字段 / 多索引 时,segment 内多份索引的生命周期如何与单次 handoff 对齐(第 10、18 篇)。
  3. 与湖表 snapshot 的跨系统对齐:同一 embedding 版本在 Iceberg 与 Milvus 之间如何证明一致(lakehouse/21 边界)。

十、小结

三句话小结

  1. Collection / Partition 是逻辑容器;真正调度单元是 Channel + Segment
  2. Growing 服务实时可变数据;Sealed 服务不可变历史与重索引。
  3. flush → index/compaction → handoff 把数据从流式侧迁到 Query Node 历史路径。

本机实验边界(2026-07-25)

写作机为 2 核 / ~2 GiB、无 Docker,无法起 Milvus standalone。可复现脚本已提供:reproduce/exp_03_segment_lifecycle.sh。在本机执行时打印 BOUNDARY: docker not found 并以退出码 2 结束——未观测到真实 Growing/Sealed 状态列表,正文也不伪造。在 ≥4 GiB + Docker + MILVUS_URI 的环境按脚本注释复现即可。

下一篇可先读写路径深化:Proxy 与 Coordinator,或 Streaming Node 与 Woodpecker WAL


参考资料

  1. Milvus Documentation v2.6.x, Data Processing(insertion、Growing/Sealed、flush、index building、query、handoff)。
  2. Milvus Documentation v2.6.x, Architecture Overview(四层、insert/search 示例流)。
  3. Milvus Documentation v2.6.x, Streaming Service(VChannel、Message、WAL 组件与 segment 生命周期策略)。
  4. milvus-io/milvus v2.6.21internal/datacoord/segment_info.goNewSegmentInfoSegmentState_Growing、channel/collection 二次索引)。
  5. Wang et al., Milvus: A Purpose-Built Vector Data Management System, SIGMOD 2021(动态向量数据管理系统动机)。
  6. 复现脚本:reproduce/exp_03_segment_lifecycle.sh(需 Docker Milvus;本写作机未跑通)。
  7. 第 1 篇 全景系列 index

返回 系列目录 | 上一篇:ANN 工程接口 | 下一篇:Proxy 与 Coordinator

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-12 · database / storage

【向量检索引擎】Query Node 与 Segcore:段级 search 如何执行

按 Milvus 2.6.x Data Processing 与 Architecture 拆解 Query Node 对 Sealed 的加载与段级检索,说明 Streaming Node 上 Growing 路径与 Query Delegator 如何拼成一次 search,用最小故事与常见误解钉住 Segcore 与 Knowhere 的层次边界。

2026-07-12 · database / storage

【向量检索引擎】Data Node:compaction 与 index build

说明 Milvus 2.6.x 中 Data Node 作为离线 Worker 如何承接 Coordinator 下发的建索引与 compaction,输入输出如何进出对象存储;用最小故事、常见误解与队列积压图说明索引堆积如何拖垮查询新鲜度与资源争用。


By .