Milvus TruncateCollection 设计深度解析:在不删除集合的前提下全量清空数据的分布式 DDL 实践
Milvus TruncateCollection 设计深度解析在不删除集合的前提下全量清空数据的分布式 DDL 实践【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus导读TruncateCollection是 Milvus 提供的一项清空集合内全部数据、但完整保留集合 Schema、索引与配置的能力适用于测试环境重置、周期性数据重灌、脏数据整体清除等场景。本文以 20260129-truncate_collection.md 设计文档为骨架对照当前开源仓库的实际实现源码逐层拆解该特性从 Proxy → RootCoord → StreamingNode → DataCoord → QueryCoord 的完整调用链、segment 判定删除的时间戳语义、压缩防护机制以及查询可见性刷新原理。读完本文你将掌握该功能为何被设计为 DDL 操作、各个组件在其中的精确职责以及实现中的关键边界条件与可验证的源码证据位置。特性概述清空数据而不是删除集合设计文档对特性的定义非常明确Feature在不 drop 集合本身的前提下清除集合内的全部数据Clear all data within a collection without dropping the collection itself与之配套的工程诉求清空操作必须高效、且不破坏集合的结构性资产。在向量数据库的实际使用中清空集合与重建集合往往产生截然不同的成本drop 集合会连带丢弃 schema、索引参数、分区、别名、加载状态等元数据重灌数据时还需要重建索引与重新加载代价高昂且容易引发误操作。TruncateCollection的设计目标因此被归纳为见原文档 Goals提供高效移除集合内全部数据的TruncateCollectionAPI保留集合的 schema、索引与配置保证跨分布式组件的数据一致性。用户视角上该操作等同于数据清零其语义边界是要确保 truncate 之前已写入的全部数据不可见、不再被查询与检索命中而 truncate 之后新写入的数据不受影响。总体设计思路当作一次分布式 DDL 来编排设计文档给出的基本方案Basic Approach是把TruncateCollection视为 DDL数据定义语言操作在既有的 DDL 框架内完成主要包含三个步骤StreamingNodeflush 全部 growing segments把内存态数据落盘DataCoord删除所有更新时戳早于 truncate 消息的 segmentsQueryCoord触发currentTarget上的视图更新使已删除的数据在查询侧不可见。之所以选择 DDL 路径而不是直接删文件或批量标记删除原因在于 Milvus 的 segment 生命周期是被多组件共享元数据严格管理的分布式状态机数据落盘由 StreamingNode 的写缓冲负责segment 元数据以 DataCoord 为准查询可见性由 QueryCoord 的 target 视图驱动。任何一个环节跳过都会留下元数据与实际数据不一致或查询仍能命中已被清空数据的窗口。从当前仓库的入口代码可以印证这条路径确实挂在 DDL 链路上mix_coord.go 中TruncateCollection直接被转发给rootcoordServer.TruncateCollection即 RootCoord 是真正的编排者而 RootCoord 侧入口 root_coord.go 与其它 DDL如 DropCollection一样先做健康检查并上报metrics.RootCoordDDLReqCounter随后走startBroadcastWithCollectionLock WAL 广播的标准 DDL 路径。使用约束与一致性边界原文档给出了三条硬性约束它们是后续一切设计取舍的出发点不得阻塞 RootCoord 或其它组件——truncate 是长耗时操作需要等 flush、等删除、等视图更新若持有全局锁会让整个集群的 DDL 停摆不得阻塞其它集合、甚至当前集合上的读写——清除数据不应影响并发写入与查询的可用性truncate 进行期间不允许对目标集合执行 DDL——避免与 CreateIndex、AlterCollection、DropPartition 等操作产生元数据竞争。约束 3 在源码中有直接体现在消息类型定义 message_type.go 中MessageTypeTruncateCollection与MessageTypeDropCollection一样被标记为ExclusiveRequired: true意味着该消息在同一资源上的广播与写入具备互斥语义同时 RootCoord 广播前会申请数据库共享锁 集合独占锁详见下文DDL 资源锁隔离一节。约束 1、2 则决定了 truncate 必须走异步广播 回调的编排方式而不是让 RootCoord 同步阻塞等待每个组件依次完成。端到端调用时序与组件协作设计文档提供了一张跨组件的交互时序图这里完整保留并做注释箭头方向自上而下表示时间推进┌──────┐ ┌───────┐ ┌────────────┐ ┌───────────┐ ┌────────────┐ ┌─────────────┐ │ User │ │ Proxy │ │ Root Coord │ │ Streaming │ │ Data Coord │ │ Query Coord │ └──┬───┘ └───┬───┘ └─────┬──────┘ │ Node │ └─────┬──────┘ └──────┬──────┘ │ │ │ └─────┬─────┘ │ │ │ truncate │ │ │ │ │ │ collection │ │ │ │ │ │───────────│ │ │ │ │ │ │ truncate │ │ │ │ │ │ collection │ │ │ │ │ │─────────────│ │ │ │ │ │ │ append wal │ │ │ │ │ │────────────────│ │ │ │ │ │ │ flush segments │ │ │ │ │ │───────────────│ │ │ │ │ fast ack │ │ │ │ │ │────────────────│ │ │ │ │ │ │ │ │ │ │ │ drop segment │ │ │ │ │ │─────────────────────────────────│ │ │ │ │ │ │ filter segment │ │ │ │ │ │ to drop │ │ │ │ wait for view to update │ │ │ │ │─────────────────────────────────────────────────────│ │ │ │ │ │ │ │ │ │ │ │ update │ │ │ │ │ │ currentTarget │ │ │ │ │ │ │对照当前仓库源码这张图里的Root Coord编排逻辑实际落在 ddl_callbacks_truncate_collection.go 中的两个关键函数上broadcastTruncateCollection构造 truncate 广播消息并写入 WALtruncateCollectionV2AckOnceCallback消息被 ack 一次时即触发尽快通过BeginTruncateCollection在元数据上打上正在 truncate标记抢在 flush / compaction 并发之前锁住压缩truncateCollectionV2AckCallback等待所有 channel flush 完成sync-up 语义后逐 vchannel 收集 flush 时戳依次完成「DropSegmentsByTime → ManualUpdateCurrentTarget → 元数据收尾 → 广播 altered collection」。这种先快速 ack 上锁、再最终 ack 执行删除的两段式回调正是满足上文不阻塞组件、但保证一致性约束的编排技巧耗时最长的 flush 等待发生在 StreamingNode / DataCoord 内部RootCoord 通过回调机制在正确时机接管后续动作而非用长事务把所有组件串行卡死。广播消息写入的 channel 范围一个值得注意的实现细节truncate 消息并非只写业务 vchannel还会写入ControlChannel。在broadcastTruncateCollection中channels 的构造是control channel 集合的全部 virtual channelchannels : make([]string, 0, len(coll.VirtualChannelNames)1) channels append(channels, streaming.WAL().ControlChannel()) channels append(channels, coll.VirtualChannelNames...) msg : message.NewTruncateCollectionMessageBuilderV2(). WithHeader(header). WithBody(body). WithBroadcast(channels, message.OptBuildBroadcastAckSyncUp()). MustBuildBroadcast() if _, err : broadcaster.Broadcast(ctx, msg); err ! nil { return err }该处可见实际源码ddl_callbacks_truncate_collection.go其中OptBuildBroadcastAckSyncUp()保证了全 channel 对齐后才执行最终回调的语义。RootCoord 会先从元数据取出coll.DBID与coll.CollectionID填入消息 header而无需依赖名字做后续匹配——这为后续 ack 回调的高效处理提供了结构化 ID。对外 API 与消息协议定义gRPC / REST 入口TruncateCollection被定义在 RootCoord 的 RPC 服务中root_coord.proto注释即点明用途This method is used to clear a collection请求携带集合名返回TruncateCollectionResponserpc TruncateCollection(milvus.TruncateCollectionRequest) returns (milvus.TruncateCollectionResponse) {}除 gRPC 外当前仓库还提供了 RESTful v2 入口在 handler_v2.go 的路由表中/v2/vectordb/collections/truncate被映射到TruncateCollection动作其 HTTP 处理器同文件 L1013-L1017将 HTTP 请求转换为带DbName、CollectionName的milvuspb.TruncateCollectionRequest后走统一 RPC 链路req : milvuspb.TruncateCollectionRequest{ DbName: dbName, CollectionName: getter.GetCollectionName(), }也就是说对用户而言该能力的触发形态与 drop/load 等集合级操作一致——指定 db collection 即可。流式消息的 Protobuf 定义truncate 命令本身通过 WAL 消息下发到各 shard设计文档给出的消息结构如下// TruncateCollectionMessageHeader is the header of truncate collection message. message TruncateCollectionMessageHeader { int64 db_id 1; string db_name 2; int64 collection_id 3; string collection_name 4; repeated int64 segment_ids 5; } // TruncateCollectionMessageBody is the body of truncate collection message. message TruncateCollectionMessageBody { }对照当前仓库的 messages.proto可以发现实现侧对 header 做了精简演进实际消息只保留了db_id、collection_id与repeated segment_ids三个字段db_name/collection_name不再随消息传递RootCoord 在构造消息前已通过名字查得 ID。而segment_ids字段在 RootCoord 构造消息时为空真正的填充发生在 StreamingNode 的 shard 拦截器里见下文用于标记本 shard 在 truncate 点之前需要被 flush 的 segments这是实现与设计一致且更精简的关键点。各组件实现剖析Proxy任务封装与入口校验Proxy 侧把 truncate 封装为一个标准 DDL 任务truncateCollectionTask定义在 task.go。任务结构体持有*milvuspb.TruncateCollectionRequest、MixCoord 客户端引用与结果对象并实现了Name()返回TruncateCollectionTaskName、OnEnqueue()将消息类型设置为MsgType_TruncateCollection等任务接口。其PreExecute阶段除了常规的集合名校验外还包含了外部集合External Collection的拦截由于外部集合的真实数据由用户侧对象存储持有、仅通过RefreshExternalCollection物化 segment 元数据对它们执行 truncate 要么静默无效误导用户要么擦除生成的 segment 元数据却留下数据源本体状态不一致因此这类集合会被拒绝 truncate见 task.go 附近注释与实现。这也再次印证了文档强调的数据一致性目标在具体边界上的落地方式。此外database_interceptor.go 会在请求DbName为空时自动填入当前上下文或默认库的库名保证 truncate 始终在正确的 database 作用域内执行。RootCoordDDL 编排与 ack 回调中枢RootCoord 是 truncate 的唯一编排者核心逻辑集中在 ddl_callbacks_truncate_collection.go。三个关键函数形成了清晰的分层broadcastTruncateCollection——入口动作加锁、查集合、构造并广播 truncate 消息truncateCollectionV2AckOnceCallback——ack once回调在广播被 ack 后立即执行collectionID : msg.Header().CollectionId if err : c.meta.BeginTruncateCollection(ctx, collectionID); err ! nil { ... } if err : c.broker.BroadcastAlteredCollection(ctx, collectionID); err ! nil { ... }其代码注释解释了为何必须在最终删除前就禁止压缩truncate 之后某个 vchannel 可能 flush 出顺序在 truncate 之后的新 segment它们本不该被删除但若并发 compaction 把truncate 前的旧段与truncate 后的新段合并成一个新段则该新段无法作为整体被删除从而破坏 truncate 的语义。因此在这里通过BeginTruncateCollection在元数据上置位并BroadcastAlteredCollection通知 DataCoord 刷新 meta 缓存。truncateCollectionV2AckCallback——全量 sync-up回调等待各 channel 的 flush 完成后执行真正的清理收集每个非 control channel 的 flush 时戳flushTsList[vchannel] result.TimeTick调用mixCoord.DropSegmentsByTime(ctx, header.CollectionId, flushTsList)让 DataCoord 按时间删除段调用mixCoord.ManualUpdateCurrentTarget(ctx, header.CollectionId)让 QueryCoord 主动刷新查询视图调用meta.TruncateCollection(ctx, result)收尾元数据清除 truncate 标记、记录每个 shard 的LastTruncateTimeTick若集合已不存在errAlterCollectionNotFound则按truncate 了一个不存在的集合忽略处理再次BroadcastAlteredCollection广播变更。BeginTruncateCollection/TruncateCollection是 RootCoord 元数据表接口的一部分见 meta_table.go其中BeginTruncateCollection被注释标记为Deprecated将在实现 ack sync-up 语义后于 3.0 移除——这反映了实现仍在向更纯粹的同步语义演进。StreamingNodeflush growing segment 并返回可删除段集合StreamingNode 侧承担了把内存态数据完整落盘的职责分为消息写入路径与 WAL flush 路径两段写入路径shard 拦截器。每个 shard 的 shard_interceptor.go 维护一个消息类型 → 处理函数的映射表MessageTypeTruncateCollection对应handleTruncateCollectionMessage。该函数同文件 L373-L382调用shardManager.FlushAndFenceSegmentAllocUntil(collectionID, msg.TimeTick())把所有包含时戳早于 truncate 消息的写入、且仍在增长中的 segment标记为待 flush并把返回的 segmentID 集合写回消息 headersegmentIDs, err : impl.shardManager.FlushAndFenceSegmentAllocUntil(header.GetCollectionId(), msg.TimeTick()) if err ! nil { return nil, status.NewUnrecoverableError(err.Error()) } header.SegmentIds segmentIDs这里的FlushAndFenceSegmentAllocUntil接口定义在 shard_manager_interface.go实现位于 shard_manager_segment.go其语义是flush 所有包含早于该时戳消息的 segment并**栅栏fence**后续的 segment 分配从而保证 truncate 点之后新分配的 segment 不会被旧消息污染。flush 路径WAL flusher。WAL flusher 消费到 truncate 消息后调用 flusherimpl/msg_handler_impl.go 的HandleTruncateCollectionfunc (impl *msgHandlerImpl) HandleTruncateCollection(flushMsg message.ImmutableTruncateCollectionMessageV2) error { vchannel : flushMsg.VChannel() if err : impl.wbMgr.SealSegments(context.Background(), vchannel, flushMsg.Header().SegmentIds); err ! nil { return errors.Wrap(err, failed to seal segments) } // ... 随后 FlushChannel将 sealed segments 的 binlog 写入对象存储并推进 channel checkpoint }即对拦截器圈定的SegmentIds执行 seal flushChannel待 checkpoint 前推后该 vchannel 的 ack 便携带了可代表truncate 点已全部落盘的 TimeTick。这一机制与设计文档Streaming NodeFlush all growing segments的描述完全对应而哪些段要被 flush由消息 header 中的segment_ids每个 shard 各自填充精确闭环。DataCoord等待落盘 checkpoint 后按时间戳删除 segmentDataCoord 是段元数据的权威方通过 services.go 中的DropSegmentsByTime承接删除动作func (s *Server) DropSegmentsByTime(ctx context.Context, collectionID int64, flushTsList map[string]uint64) error { if err : merr.CheckHealthy(s.GetStateCode()); err ! nil { return err } for channelName, flushTs : range flushTsList { // wait until the checkpoint reaches or exceeds the flush timestamp err : s.meta.WatchChannelCheckpoint(ctx, channelName, flushTs) if err ! nil { return err } // drop segments that were updated before the flush timestamp err s.meta.TruncateChannelByTime(ctx, channelName, flushTs) if err ! nil { return err } } return nil }它逐 vchannel 执行两个阶段WatchChannelCheckpointmeta.go条件等待该 channel 的 checkpoint 时戳达到 / 超过传入的 flushTs本质上是在等待 StreamingNode 的落盘结果真正同步到 DataCoord 的 channel checkpoint 视图。这与设计文档中Data CoordinatorWait for flush to complete的要求一致并且采用条件变量channelCPs.cond而非轮询避免空转。TruncateChannelByTimemeta.go遍历该 channel 上的健康 segment凡是生效 DML 时戳 ≤ flushTs 且状态尚未 Dropped的克隆并标记为SegmentState_Dropped收集成批次后通过catalog.AlterSegments持久化到 etcd——注意这是批量元数据操作而非逐个删段正是文档所说efficiently remove的关键。在删除判定中使用的segmentEffectiveDmlTs定义于 segment_info.go它优先取CommitTimestamp用于 import / backfill 等提交后才正式存在的 segment否则回退到DmlPosition.Timestamp。这个commit 时戳覆盖逻辑避免了导入型 segment 因 DML 时戳较早而在 truncate / GC 中被误删。QueryCoord手动刷新 currentTarget保证查询不可见性删除元数据之后若查询侧仍持有旧的 target 视图客户端依然可能命中已删除段的数据。为此设计文档要求 QueryCoord触发 currentTarget 的视图更新。当前仓库的实现位于 querycoordv2/services.go 的ManualUpdateCurrentTargetfunc (s *Server) ManualUpdateCurrentTarget(ctx context.Context, collectionID int64) error { ... // Check if collection is loaded percentage : s.meta.CalculateLoadPercentage(ctx, collectionID) if percentage 0 { mlog.Info(context.TODO(), collection not loaded, skip ManualUpdateCurrentTarget) return nil } err : job.WaitCurrentTargetUpdated(ctx, s.targetObserver, collectionID) ... }两点值得注意若集合未加载load percentage 0直接跳过——未加载的集合本就没有查询视图可刷新等待只会徒增延迟对已加载集合调用job.WaitCurrentTargetUpdated驱动 target observer 触发一轮完整的 target 更新并等待其结束从而保证调用方返回后查询路由已基于 DataCoord 的最新 segment 集合被 truncate 删除的段已被剔除。MixCoord组合网关的转发封装在混部架构下RootCoord 回调并不直接连接各 Coordinator而是通过 MixCoord 的组合服务转发。见 mix_coord.gofunc (s *mixCoordImpl) TruncateCollection(ctx, req) (*milvuspb.TruncateCollectionResponse, error) { return s.rootcoordServer.TruncateCollection(ctx, req) } func (s *mixCoordImpl) DropSegmentsByTime(ctx, collectionID, flushTsList) error { return s.datacoordServer.DropSegmentsByTime(ctx, collectionID, flushTsList) } func (s *mixCoordImpl) ManualUpdateCurrentTarget(ctx, collectionID) error { return s.queryCoordServer.ManualUpdateCurrentTarget(ctx, collectionID) }distributed/mixcoord/service.go 进一步把它暴露为 gRPC 服务使分布式部署下任意组件的远程调用都能命中同一组合网关。这也解释了为何 RootCoord 回调中统一使用c.mixCoord接口而无需关心 DataCoord / QueryCoord 是本地进程还是远端服务。关键设计细节DDL 资源锁隔离在既有 DDL 框架中DDL 请求写入 WAL 前需要获取资源锁。锁按层级database / collection与类型shared / exclusive划分truncate 通过如下代码申请db 共享锁 集合独占锁见设计文档引用源码见 ddl_callbacks_truncate_collection.go 所调用的封装func (*Core) startBroadcastWithCollectionLock(ctx context.Context, dbName string, collectionName string) (broadcaster.BroadcastAPI, error) { broadcaster, err : broadcast.StartBroadcastWithResourceKeys(ctx, message.NewSharedDBNameResourceKey(dbName), message.NewExclusiveCollectionNameResourceKey(dbName, collectionName), ) if err ! nil { return nil, errors.Wrap(err, failed to start broadcast with collection lock) } return broadcaster, nil }其隔离效果可以精确归纳为同一 database 内的其它 DDL 不受影响db 上只持共享锁满足不阻塞其它集合操作目标集合上的其它 DDL 被独占锁串行化满足truncate 期间禁止对该集合执行 DDLDMLinsert / delete / upsert不需要集合级独占锁因此 truncate 期间该集合的读写仍可进行——新写入的数据进入 truncate 点之后的新 segment天然不会被本次删除波及。Segment 元数据状态推进步骤设计文档梳理了 truncate 过程中 segment 元数据在三个组件间的推进链路StreamingNode内存态更新内存中的 segment 状态并设置 flush 时戳。设计文档给出了内存段结构示例segmentID / partitionID / state / startPosition / checkpoint / bufferRows / binlogs / statslogs ...这一结构在当前仓库对应 StreamingNode 写缓冲侧的 segment 描述——每个增长中的段都记录自己已消费的 checkpoint作为后续是否落在 truncate 点之前的判定输入。StreamingNode落盘态WriteNode 完成实际 flush 后通知 DataCoord 更新状态。落盘段带有DmlPosition、binlog / statslog / deltalog 路径与行数统计成为 DataCoord 元数据与查询可见性的唯一事实来源。DataCoord按时间戳批量把相关 segment 置为 Dropped前述TruncateChannelByTime最终由 GC 流程回收其对象存储文件。QueryCoord触发一次 target 视图更新从 DataCoord 拉取最新 segment 集合前述ManualUpdateCurrentTarget。如何判定哪些段该删时间戳语义判定准则依赖两条时间戳事实truncate 消息写入 WAL 时被分配一个单调递增的时间戳msg.TimeTick每个 segment 的DmlPosition记录其已消费数据的最大位置其中的Timestamp表示该段中最新数据的时戳。设计文档给出的MsgPosition结构ChannelName / MsgID / MsgGroup / Timestamp即 Milvus 全链路位置推进的基础类型若 segment 经历了 compaction新段的DmlPosition取参与合并旧段中的最小值保证时间戳判定对合并产物依然单调安全。由此得出文档定义的删除条件段非空且DmlPosition.Timestamp msg.Timestamp。落到当前实现的删除动作TruncateChannelByTime中等价条件演化为segmentEffectiveDmlTs(segment) flushTsflushTs 为每 vchannel flush 完成后的 ack 时戳见 meta.go并额外叠加了CommitTimestamp覆盖逻辑保护导入段。整体上truncate 点之前产生的数据所在的段都会进入删除集合而 truncate 点之后的新数据因 DML 时戳更大而天然豁免。压缩防护CollectionOnTruncatingKey 与 LastTruncateTimeTick压缩是本设计中最需要防御的并发陷阱若 compaction 在 truncate 期间把新旧段合并产物段将同时含该删的旧数据与该留的新数据导致无法整体删除。设计文档给出的方案是为集合增加一个内部属性CollectionOnTruncatingKeytruncate 消息写入 WAL 后置为1truncate 完成后置回0compaction 校验流程中若发现该 key 处于开启状态则拒绝该集合的压缩请求。当前仓库对这套方案的实现路径清晰可见key 定义于 pkg/common/common.goCollectionOnTruncatingKey collection.on.truncating注释明确写着when collection is on truncating, forbid the compaction of current collectionRootCoord 元数据侧BeginTruncateCollection 幂等地把它写入集合 Properties值为1TruncateCollection 收尾时再delete该 key并同时把每个 shard 的LastTruncateTimeTick更新为对应 channel 的 ack TimeTickDataCoord 侧压缩触发前的开关判定 getCollectionAutoCompactionEnabled 会先检查该 key——只要存在无论值为何即返回false禁用自动压缩compaction_trigger.go 中的isCollectionAutoCompactionEnabled再叠加外部集合 / 全局配置等判断每 shard 的最近一次 truncate 时戳LastTruncateTimeTick会随集合元数据持久化字段定义见 metastore/model/collection.go新建集合时初始化为 0见 ddl_callbacks_create_collection.go为后续基于 truncate 边界的时间过滤提供依据。另外CollectionOnTruncatingKey与BeginTruncateCollection接口在源码注释中均被标注为Deprecated将在实现 ack sync-up 语义后于 3.0 移除——也就是说随着等所有 channel 全部 flush 完成后再统一清理的强同步语义逐渐成熟依赖临时属性来防压缩的做法有望被更结构化的机制取代。这是从代码注释可以推断的演进方向读者在使用该特性时不必依赖该内部 key 做任何外部假设。源码验证与测试覆盖本特性在仓库中拥有成体系的单元测试可作为理解与验证的入口关注点测试文件验证内容StreamingNode flush 语义flusherimpl/msg_handler_impl_test.goHandleTruncateCollection在SealSegments失败、FlushChannel失败、正常三种路径下的行为Shard 拦截器shard_interceptor_test.goFlushAndFenceSegmentAllocUntil返回 segmentIDs 并回填 header、错误传播RootCoord 元数据状态机meta_table_test.gotruncate 期间CollectionOnTruncatingKey1的写入、reload 后仍保留、完成后 key 被删除且LastTruncateTimeTick被正确记录删除时戳语义commit_timestamp_test.go 与 segment_info_test.gosegmentEffectiveDmlTs在普通段取 DML 时戳、导入段优先取 commit 时戳的行为自动压缩开关util_test.gotruncate key 存在时getCollectionAutoCompactionEnabled返回 false这些测试从三个维度消息处理、元数据标记、时戳过滤把数据删除的安全边界固定下来任何破坏 truncate 语义的改动都会在此处暴露。设计演进与边界说明将设计文档与当前仓库对照可以看到实现相对设计稿的若干演进与需要读者注意的边界消息头字段精简设计稿中的db_name/collection_name字段在 messages.proto 中已被移除消息只携带 ID 与逐 shard 回填的segment_ids删除判定放宽为设计稿写作DmlPosition.Timestamp msg.Timestamp实际实现以每 channel 的 flush ack 时戳为基准并允许等号命中同时叠加 commit 时戳覆盖——语义上仍保证truncate 点之前的数据全部清除压缩防护的实现位置当前代码把CollectionOnTruncatingKey的检查落在 DataCoord 自动压缩开关判定中并与集合级独占锁形成双保险该 key 及BeginTruncateCollection均被标注 Deprecated暗示未来将收敛到同步 ack 语义空段与 GC 衔接被标记 Dropped 的 segment 元数据之后会由 DataCoord 的垃圾回收流程清理对象存储文件segmentEffectiveDmlTs同时被 GC 判定复用见 garbage_collector.go因此 truncate 不会留下孤立的对象文件外部集合不可 truncate源于 Proxy 层的显式拒绝属于该能力对外部表数据由用户对象存储托管模型下的有意边界。小结TruncateCollection是 Milvus 分布式 DDL 框架上一个极具代表性的用例它用异步广播 两段式 ack 回调替代全局长事务用WAL 时间戳 每 channel flush 时戳刻画精确的数据删除边界用元数据置位 自动压缩开关防御并发 compaction 对段整体性的破坏再用manual update current target保证查询视图即时收敛。其最终效果正如设计文档 Summary 归纳的四点一致性所有 segment 先落盘再删除、隔离性truncate 期间其它读写不受阻塞、可见性查询结果即时反映清空状态、安全性压缩被暂时阻断以避免数据不一致。如果你正在为数据重灌或环境重置设计存储层清理流程本文的调用链、时间戳判定与源码证据可以作为你在 Milvus 中正确使用并深入排查该功能的完整参考。【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考