LoadDiff 机制详解:Milvus Segment 自管理加载与 Reopen 增量更新架构
LoadDiff 机制详解Milvus Segment 自管理加载与 Reopen 增量更新架构【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus本文基于 Milvus 查询节点QueryNode与 segcore 的 LoadDiff 设计文档系统讲解 Milvus 如何把 sealed segment 的加载编排从 Go 层下沉到 C segcore 层使 segment 自治管理自身的加载过程并通过 diff 计算支持不重载整段的增量更新Reopen。读完你将对SegmentLoadInfo、LoadDiff、ApplyLoadDiff、Storage V1/V2/V3 存储模式适配以及 QueryCoord 侧的触发链路形成完整的工程认知。背景与动机segment 加载为什么需要自己管理自己Milvus 的查询节点需要把位于对象存储上的 sealed segment含字段数据、向量/标量索引、删除记录、文本/JSON 统计等加载进内存以服务查询。在引入 LoadDiff 之前整条加载链路的编排职责全部集中在 Go 层。旧架构Go 层大包大揽segcore 被动接收根据设计文档docs/design-docs/design_docs/segcore/20260204-loaddiff_based_segment_load.md旧设计中 Go 层segment_loader.go需要负责资源估算与申请按 segment 大小与字段配置申请内存/磁盘配额并发控制加载编排走Load → LoadSegment → loadSealedSegment/LoadMultiFieldData这条较长调用链等待与同步。而 C segcore 层完全被动被动接收数据LoadFieldData、LoadIndex、LoadDeletedRecord不具备自主调度能力。由此带来的典型问题是加载相关状态已加载哪些字段、哪些索引、索引是否包含原始数据 raw data散落在 Go 侧的对象中Go 与 C 之间围绕下一步该传什么需要大量跨语言调用与状态同步一旦目标数据源发生局部变化例如新增一个字段、索引从仅有向量变为携带原始数据、manifest 更新往往只能整体卸载并重载整段开销大、链路长。新架构的三大目标让segment 自己管理加载对应 issue #45060支持在 QueryNode 上Reopen增量重开segment对应 issue #46358把从目标状态推导需要做哪些变更抽象为统一的LoadDiff 差异计算让初始加载Load与增量更新Reopen走同一套执行引擎。说明以下关键实现均可在仓库中找到实证核心路径位于internal/core/src/segcore/C segcore与internal/querycoordv2/、internal/querynodev2/Go 层。新架构总览三层协作 C API 桥接设计文档给出了清晰的端到端分层结构┌─────────────────────────────────────────────────────────────────────────────┐ │ QueryCoord │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ SegmentChecker │ │ │ │ - Detects segment state changes (lacks, redundancies, updates) │ │ │ │ - Creates Load/Reopen/Reduce tasks based on diff with target │ │ │ │ - getSealedSegmentDiff() checks ManifestPath changes │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ SegmentLoadInfo ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ QueryNode (Go) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ segmentLoader │ │ │ │ - Resource estimation and protection │ │ │ │ - Delegates actual loading to segcore via C API │ │ │ │ - ReopenSegments() for incremental updates │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ LocalSegment │ │ │ │ - Load() / Reopen() pass SegmentLoadInfo to C │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ C API (SegmentLoad / ReopenSegment) ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Segcore (C) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ SegmentLoadInfo │ │ │ │ - Wraps protobuf SegmentLoadInfo with caching │ │ │ │ - ComputeDiff(new_info) → LoadDiff │ │ │ │ - GetLoadDiff() → LoadDiff (for initial load from empty state) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ ChunkedSegmentSealedImpl │ │ │ │ - SetLoadInfo(proto) stores segment load configuration │ │ │ │ - Load() calls GetLoadDiff() ApplyLoadDiff() │ │ │ │ - Reopen(new_info) calls ComputeDiff() ApplyLoadDiff() │ │ │ │ - ApplyLoadDiff() executes incremental changes │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘各层职责如下QueryCoord调度中枢SegmentChecker周期性对照分布式目标target与节点当前分布dist发现缺失lacks、冗余redundancies与需要更新的 segmentupdates据此产出 Load / Reopen / Reduce 任务并把SegmentLoadInfo下发到 QueryNode。实际实现见 internal/querycoordv2/checkers/segment_checker.go。QueryNodeGo 协调层segmentLoader只负责资源估算与保护真正的加载通过 C API 委托给 segcore对增量场景调用ReopenSegments()。入口可参考 internal/querynodev2/handlers.go 中由LoadSegmentsRequest触发的reopenSegments逻辑其接口定义位于 internal/querynodev2/segments/segment_loader.go。Go 侧LocalSegment的Load()/Reopen()负责把SegmentLoadInfo透传给 C。SegcoreC 执行层SegmentLoadInfo包装 protobuf 消息并提供差异计算能力ChunkedSegmentSealedImpl是 sealed segment 的核心实现负责SetLoadInfo、Load、Reopen与ApplyLoadDiff的具体执行。值得一提这条下沉链路的落地对应一批已合入的 PR#45061 新增NewSegmentWithLoadInfoAPI、#45488 把加载逻辑从 Go 层迁入 segcore、#46359 支持因数据/schema 变化触发的 reopen、#46394 让 QueryCoord 在 manifest 路径变化时触发 reopen、#46536 统一 Load 与 Reopen、#46598 处理 v1 旧 binlog 格式、#47061 支持 index-含-raw-data 字段的懒加载、#47412 把默认值填充纳入 LoadDiff。核心组件一SegmentLoadInfo 类加载信息包装与缓存设计文档中该类位于 internal/core/src/segcore/SegmentLoadInfo.h 与同目录SegmentLoadInfo.cpp是一个对 protobufSegmentLoadInfo消息的 C 包装层能力包括访问器GetSegmentID()、GetNumOfRows()、GetIndexInfos()、GetBinlogPaths()等缓存内部通过BuildCache()构建按字段快速查找的结构文档注释称其 cache 为对可变计算的 memoization见 SegmentLoadInfo.h 中GetColumnGroups()的可重入说明索引转换把FieldIndexInfo转换为带 mmap 配置的LoadIndexInfo差异计算ComputeDiff()与GetLoadDiff()。设计文档给出的类骨架如下class SegmentLoadInfo { public: explicit SegmentLoadInfo(const ProtoType info, SchemaPtr schema); // Compute diff between current and new load info (for Reopen) LoadDiff ComputeDiff(SegmentLoadInfo new_info); // Compute diff from empty state (for initial Load) LoadDiff GetLoadDiff(); // Column groups support (Storage V3) std::shared_ptrmilvus_storage::api::ColumnGroups GetColumnGroups(); private: void BuildCache(); void ComputeDiffIndexes(LoadDiff diff, SegmentLoadInfo new_info); void ComputeDiffBinlogs(LoadDiff diff, SegmentLoadInfo new_info); void ComputeDiffColumnGroups(LoadDiff diff, SegmentLoadInfo new_info); void ComputeDiffReloadFields(LoadDiff diff, SegmentLoadInfo new_info); };从实际源码SegmentLoadInfo.h看私有 diff 计算函数比文档骨架更丰富还包括ComputeDiffDefaultFields默认值字段差异、ComputeDiffTextIndexes文本索引差异与ComputeDiffJsonKeyStatsJSON key 统计差异可见差异类型会随功能演进持续扩展。核心组件二LoadDiff 结构体变更清单LoadDiff表示两个 segment 加载状态之间的差异是整个机制的变更清单。设计文档给出基础版struct LoadDiff { // Indexes to load (field_id - list of LoadIndexInfo) std::unordered_mapFieldId, std::vectorLoadIndexInfo indexes_to_load; // Binlogs to load (Storage V1/V2) std::vectorstd::pairstd::vectorFieldId, proto::segcore::FieldBinlog binlogs_to_load; // Column groups to load (Storage V3 manifest mode) std::vectorstd::pairint, std::vectorFieldId column_groups_to_load; // Column groups for lazy loading (index has raw data) std::vectorstd::pairint, std::vectorFieldId column_groups_to_lazyload; // Fields to reload (when index raw data availability changes) std::vectorFieldId fields_to_reload; // Fields to fill with default values (schema evolution) std::vectorFieldId fields_to_fill_default; // Indexes to drop std::setFieldId indexes_to_drop; // Field data to drop std::unordered_setFieldId field_data_to_drop; // Manifest update flag bool manifest_updated false; std::string new_manifest_path; bool HasChanges() const; std::string ToString() const; // For debugging };实际实现SegmentLoadInfo.h在文档基础上还引入了大量replace 类与细分统计类条目每个条目的语义注释都值得精读indexes_to_loadvsindexes_to_replace前者用于字段尚无索引的新增加载后者用于字段已有索引时的替换加载binlogs_to_load/binlogs_to_replace仅在 current 与 new 都处于 binlog 模式时填充column_groups_to_load/column_groups_to_replace对应字段新建/字段在列组间移动或列组数据变化的场景同一个 group 可因不同配置重复出现column_groups_to_lazyload/column_groups_to_lazyreplace懒加载路径含index 携带 raw data的字段json_indexes_to_drop由于 JSON 字段可含多个嵌套路径索引字段级 drop 会丢失保留兄弟路径所需的标识故按field_id - 嵌套路径集合精确下发文本/JSON 统计系列text_indexes_to_load从预构建文件装载、text_indexes_to_create从 raw data 现场创建服务于 enable_match 字段、json_stats_to_load/replace/dropload_external_manifest外部集合external collection的列名是 parquet 字段名而非数字 field_id应绕过ComputeDiffColumnGroups在ApplyLoadDiff中直接LoadColumnGroups(manifest_path)。HasChanges()会聚合上述全部条目含manifest_updated与load_external_manifestToString()则为每个条目输出人类可读摘要方便定位这个 reopen 到底改了什么。一个需要特别留意的约束代码注释中明确写出binlog 模式与 manifest 模式之间不支持跨类别变更——SegmentLoadInfo只能在同一类别内演进即 binlog → binlog或 manifest → manifest。核心组件三ApplyLoadDiff —— 统一执行增量变更设计文档给出的执行骨架位于 internal/core/src/segcore/ChunkedSegmentSealedImpl.cppvoid ChunkedSegmentSealedImpl::ApplyLoadDiff( SegmentLoadInfo segment_load_info, LoadDiff diff, milvus::OpContext* op_ctx) { // 1. Load new indexes if (!diff.indexes_to_load.empty()) { LoadBatchIndexes(trace_ctx, diff.indexes_to_load, op_ctx); } // 2. Reload fields (MUST before drop index) if (!diff.fields_to_reload.empty()) { ReloadColumns(diff.fields_to_reload, op_ctx); } // 3. Drop indexes (MUST after reload to maintain data availability) if (!diff.indexes_to_drop.empty()) { for (auto field_id : diff.indexes_to_drop) { DropIndex(field_id); } } // 4. Load column groups (eager lazy) if (!diff.column_groups_to_load.empty()) { LoadColumnGroups(..., diff.column_groups_to_load, true, op_ctx); } if (!diff.column_groups_to_lazyload.empty()) { LoadColumnGroups(..., diff.column_groups_to_lazyload, false, op_ctx); } // 5. Load field binlogs (Storage V1/V2) if (!diff.binlogs_to_load.empty()) { LoadBatchFieldData(trace_ctx, diff.binlogs_to_load, op_ctx); } // 6. Fill default values for schema evolution if (!diff.fields_to_fill_default.empty()) { FillDefaultValueFields(diff.fields_to_fill_default); } // 7. Drop field data if (!diff.field_data_to_drop.empty()) { for (auto field_id : diff.field_data_to_drop) { DropFieldData(field_id); } } }实现层面ChunkedSegmentSealedImpl.cpp有两点超出文档骨架、需要补充的事实每个阶段都做了可取消性cancellation检查。源码在第 7243、7253、7268、7347 行等多处调用CheckCancellation(op_ctx, id_, ChunkedSegmentSealedImpl::ApplyLoadDiff())与 Go 侧的资源保护配合保证加载过程可被中止回收避免长时间占用资源。内部存在已发布状态PublishedState快照与写时复制COW提交机制。例如Load()开头会CapturePublishedState()再构造可变副本文档注释说明ApplyLoadDiff 产生的运行时更新在数据加载完成后通过 COW helper 提交保证并发读search/retrieve在加载过程中始终看到一致状态。索引/字段的 drop 也有DropIndexFromState(...)、DropFieldData(..., runtime)这类带状态快照的重载版本具体可参见 ChunkedSegmentSealedImpl.cpp 中DropFieldData与DropIndex的定义。Load 与 Reopen同一 diff 机制的两个入口这是本次设计的核心统一点初始 Load 与增量 Reopen 共用ApplyLoadDiff()执行管线差异只在于 diff 的来源。初始 Load从空状态取 diffvoid ChunkedSegmentSealedImpl::Load( milvus::tracer::TraceContext trace_ctx, milvus::OpContext* op_ctx) { auto diff segment_load_info_.GetLoadDiff(); ApplyLoadDiff(segment_load_info_, diff, op_ctx); }实际的Load()实现ChunkedSegmentSealedImpl.cpp与 Reopen 共享reopen_mutex_以保证串行化并通过CapturePublishedState()捕获快照后把已用默认值填充的字段、已创建的文本索引等运行时状态标记进可变副本再GetLoadDiff()计算从空 → 目标的完整 diff 并打日志如Loading segment {id} with {rows} rows最后交给ApplyLoadDiff。Reopen对当前状态计算增量 diffvoid ChunkedSegmentSealedImpl::Reopen( const milvus::proto::segcore::SegmentLoadInfo new_load_info, SchemaPtr new_schema) { auto current std::atomic_load(segment_load_info_); SegmentLoadInfo current_mutable(*current); SegmentLoadInfo new_seg_load_info(new_load_info, new_schema); auto diff current_mutable.ComputeDiff(new_seg_load_info); std::atomic_store(segment_load_info_, std::make_sharedconst SegmentLoadInfo(new_seg_load_info)); ApplySchemaForReopen(new_schema); ApplyLoadDiff(new_seg_load_info, diff); }从实现可以看到segment_load_info_是一个原子共享指针std::atomic_load/std::atomic_store当前加载配置会被整体替换成新配置同时ApplySchemaForReopen(new_schema)更新段内 schema最后ApplyLoadDiff只执行差异部分。源码中Reopen系列包含多个重载见 ChunkedSegmentSealedImpl.cpp分别覆盖仅 schema 变化携带 OpContext 的 schema 变化以及携带完整新 load info schema的路径。Schema 感知的 Reopen文档特别强调schema 变更不再走直接填充缺失字段的独立 sealed-segment 路径。QueryNode 会把最新集合 schema 连同SegmentLoadInfo一起传给 segcore于是ComputeDiff()在计算 load / 默认值填充 / drop 动作时能看见新增或删除的字段。对于仅由 query/retrieve 计划触发的schema-only 懒检查Reopen(SchemaPtr)会复用当前 load info与较新的 schema 做差异计算默认值填充、字段数据删除、索引删除因此都被建模进LoadDiff统一由ApplyLoadDiff()执行。这也让段内字段与查询计划所需字段的一致性维护完全收敛到 C 层。三种存储模式的 diff 适配Milvus 的对象存储布局历史上存在多种格式LoadDiff 机制对它们做了分层适配存储模式数据组织特征diff 计算入口说明Storage V1遗留 binlog每个字段拥有独立 binlog 文件child_fields为空field_id group_idComputeDiffBinlogs()文档中称为 legacy binlog另有 PR #46598 专门处理 v1 格式Storage V2列组 Column Groups多个字段可共享同一列组child_fields列出组内字段 IDComputeDiffBinlogs()仍以 binlog 路径集合表达变更Storage V3manifest 模式使用Loon manifest描述列组元数据支持index 携带 raw data字段的懒加载ComputeDiffColumnGroups()ManifestPath变化会触发 segment reopen设计文档明确指出 V3 的 diff 类型划分为加载/替换列组与懒加载/懒替换列组两对动作且该能力由 SegmentLoadInfo.cpp 中GetColumnGroups()与milvus_storage::api::ColumnGroups支撑。Proto 定义与字段演进SegmentLoadInfo的线格式定义于 pkg/proto/segcore.protoQueryCoord 调度侧还有一份同名的querypb.SegmentLoadInfo见 pkg/proto/query_coord.proto两者在跨层转发时互转。设计文档收录的消息体message SegmentLoadInfo { int64 segmentID 1; int64 partitionID 2; int64 collectionID 3; int64 dbID 4; int64 flush_time 5; repeated FieldBinlog binlog_paths 6; int64 num_of_rows 7; repeated FieldBinlog statslogs 8; repeated FieldBinlog deltalogs 9; repeated int64 compactionFrom 10; repeated FieldIndexInfo index_infos 11; string insert_channel 13; int64 readableVersion 14; int64 storageVersion 15; bool is_sorted 16; mapint64, TextIndexStats textStatsLogs 17; repeated FieldBinlog bm25logs 18; mapint64, JsonKeyStats jsonKeyStatsLogs 19; common.LoadPriority priority 20; string manifest_path 21; // Storage V3 manifest path }对照仓库实际文件pkg/proto/segcore.proto该消息已进一步演进后续新增字段包括use_take_for_output字段 22为 true 时输出字段检索改用take()API 而非bulk_subscriptestimated_bytes_per_row字段 23来自 Take API 的每行平均内存字节采样值在格式元数据缺乏 size stats 时作为回退估算commit_timestamp字段 24import / CDC segment 的事务时间戳。非零时 segcore 会覆写内存中的时间戳列使 MVCC / TTL / 删除逻辑统一按 commit 时间而非行内原始时间戳生效原文档中compactionFrom字段 10注释为 segmentIDs compacted from即该 segment 由哪些 compaction 源段合并而来。FieldIndexInfo同文件 L100-L115则描述了每个索引的index_name/indexID/buildID、index_params、index_file_paths、index_size、index_version、num_rows、current_index_version与标量索引版本等信息是 diff 中索引加载/替换/删除判定的数据基础。QueryCoord 集成谁来决定何时 ReopenSegmentChecker 与差异检测Go 侧负责发现更新需求的是 internal/querycoordv2/checkers/segment_checker.go 中的SegmentChecker。设计文档给出了关键逻辑func (c *SegmentChecker) getSealedSegmentDiff(...) (toLoad, loadPriorities, toRelease, toUpdate) { isSegmentUpdate : func(segment *datapb.SegmentInfo) bool { segInDist, existInDist : distMap[segment.ID] return existInDist segInDist.ManifestPath ! segment.GetManifestPath() } // ... detect updated segments and create reopen tasks } func (c *SegmentChecker) createSegmentReopenTasks(...) []task.Task { // Creates ActionTypeReopen tasks for updated segments }实际实现印证了这一描述getSealedSegmentDiffsegment_checker.go在判断是否需要 update 时调用了packed.CompareManifestPath(segInDist.ManifestPath, segment.GetManifestPath())segment_checker.go对dist 中段与目标段的 manifest 路径做语义比较并在日志中同时输出 distManifest 与 targetManifest而 reopen 任务最终通过task.NewSegmentAction(s.Node, task.ActionTypeReopen, s.GetInsertChannel(), s.GetID())创建segment_checker.go。新增 ActionTypeReopen为承载增量更新任务动作类型在既有基础上新增了一类const ( ActionTypeGrow ActionType iota 1 ActionTypeReduce ActionTypeUpdate ActionTypeReopen // New action type for segment reopen )任务到达 QueryNode 后由 handler 层路由到 loader 的ReopenSegmentsinternal/querynodev2/handlers.go该能力在 loader 接口ReopenSegments(ctx context.Context, loadInfos ...)中暴露internal/querynodev2/segments/segment_loader.go最终把SegmentLoadInfo交给 segcore 执行 C 侧的Reopen。执行顺序保证查询永远有可用数据源ApplyLoadDiff()内的动作顺序是正确性关键。设计文档的权威顺序如下加载/替换索引—— 新索引或替换索引最先就位重载列ReloadColumns—— 恢复因索引不再携带 raw data而失去 raw data 覆盖的字段加载/替换列组—— Storage V3 的 eager 与 lazy 两路加载/替换字段 binlog—— Storage V1/V2 数据面丢弃索引—— 只有在新数据/新索引可用后才 drop 旧索引加载文本/JSON stats 索引—— 应用预构建的 text 与 JSON 统计变更填充默认值—— 面向无数据源的 schema 演进字段从 raw data 现场创建文本索引—— 服务于没有预构建索引文件的 enable_match 字段丢弃字段数据—— 清理被删除的字段。设计文档特别强调当前实现总是先加载/替换 raw 字段数据、再 drop 旧索引因此在索引切换过程中查询永远可以回退到某个合法数据源。这个可用性保证是第 2、3、4 步必须排在 drop 动作第 5、9 步之前的根本原因。当前能力矩阵设计文档以表格形式总结了 LoadDiff 机制已覆盖的能力均标注为已实现可在 SegmentLoadInfoTest.cpp 等测试中找到对应用例FeatureStatusDescriptionInitial Load✅ Implemented从空状态经GetLoadDiff()加载 segmentIndex Load✅ Implemented加载新索引向量/标量Index Drop✅ Implemented目标中移除的索引被 dropBinlog Load✅ Implemented加载字段 binlogStorage V1/V2Column Group Load✅ Implemented加载列组Storage V3Lazy Loading✅ Implemented跳过携带 raw data 的索引字段的加载Field Reload✅ Implemented索引 raw data 可用性变化时重载字段Schema Evolution✅ Implemented为新增字段填充默认值Manifest Update✅ Implementedmanifest 路径变化时执行 reopenLegacy Format✅ Implemented兼容 V1 binlog 格式单元测试覆盖差异计算的单元测试集中在 internal/core/src/segcore/SegmentLoadInfoTest.cpp仓库中约 3955 行设计文档概括的测试维度包括索引的 diff 计算binlog 的 diff 计算覆盖 V1 与 V4 两种格式列组的 diff 计算混合格式处理V1/V2 binlog 与 V3 manifest 并存场景下的模式约束schema 演进场景新增带默认值字段、字段数据 drop、索引 drop 的组合。与之配套的段级测试还有ChunkedSegmentSealedStorageV2Test.cpp、ChunkedSegmentSealedTest.cpp、ChunkedSegmentSealedBinlogIndexTest.cpp与LoadCancellationTest.cpp等读者可沿 internal/core/src/segcore/ 目录继续深入。收益总结设计文档归纳的架构收益可概括为七点职责清晰加载逻辑完全由 segment 自己管理Go 层退化为资源看门人性能优化显著减少 Go ↔ C 的跨语言调用次数编排不再逐字段往返资源管理更精准C 层直接处理资源分配避免中间层重复估算缓存深度整合segment 可直接与缓存层交互schema 演进更主动segment 掌握自身全部信息能自主决策填充/删除增量更新局部变化新增字段、索引 raw data 状态翻转、manifest 更新无需整体卸载重载API 统一单一ApplyLoadDiff()覆盖所有加载场景Load 与 Reopen 共享一条执行管线。未来演进方向文档明确该机制的终极目标是让所有加载状态变更都走 LoadDiff。已规划的扩展方向包括在线索引构建检测到某字段新增索引 → reopen 段并加载新索引 → 若索引携带 raw data 则 drop 字段数据在线 schema 变更新增带默认值字段、drop 字段删除字段数据、修改字段属性索引类型变更在不同索引类型之间切换并处理参数变化Compaction 集成compaction 之后更新 segment、处理段合并分层存储Tiered Storage数据在不同存储层级间移动、更新存储位置。如何继续阅读源码建议按以下路径沿决策 → 编排 → 执行的调用链阅读调度与差异检测internal/querycoordv2/checkers/segment_checker.gogetSealedSegmentDiff与ActionTypeReopen任务创建线格式定义pkg/proto/segcore.protoSegmentLoadInfo、FieldIndexInfo与 pkg/proto/query_coord.protoGo 编排层internal/querynodev2/segments/segment_loader.goReopenSegments、internal/querynodev2/handlers.goC 差异计算internal/core/src/segcore/SegmentLoadInfo.h 与同目录SegmentLoadInfo.cppComputeDiff*系列、GetLoadDiffC 执行主体internal/core/src/segcore/ChunkedSegmentSealedImpl.cppLoad/Reopen/ApplyLoadDiff/DropIndex/DropFieldData测试印证internal/core/src/segcore/SegmentLoadInfoTest.cpp。提示LoadDiff 机制面向 sealed segment 的加载编排其行为与对象存储格式版本Storage V1/V2/V3、索引是否携带 raw data、schema 演进状态强相关跨 binlog/manifest 类别的变更当前不受支持阅读代码与设计时需留意上述前提。【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考