Milvus 的 Segment 与 Compaction:后台合并时的查询抖动排查

📅 发布时间:2026/9/4 22:23:01
Milvus 的 Segment 与 Compaction:后台合并时的查询抖动排查
Milvus 的 Segment 与 Compaction后台合并时的查询抖动排查在高并发生产环境中运维 Milvus 向量数据库时很多团队经常遇到一种诡异的“周期性性能毛刺”平时在线检索耗时非常稳定P99 维持在 5ms 左右但每隔十几分钟或半小时P99 延迟就会突然剧烈抖动瞬间飙升到 80ms 甚至 200ms持续一两分钟后又自动恢复正常。查看系统监控此时在线查询 QPS 并没有明显突增但磁盘 I/O 读写和 CPU 利用率却出现了一波剧烈的脉冲式高峰。去查 Milvus 的内部运行日志真相通常非常聚焦这是后台正在执行 Segment Compaction分片数据压缩与合并带来的资源争抢深入搞懂 Milvus 的 Segment 生命周期与 Compaction 底层机制是彻底根除线上查询抖动、保障 SLA 稳定的关键一步。Milvus 的数据组织Growing 与 Sealed Segment在 Milvus 架构中数据是以Segment数据段为物理单位进行管理的。每个 Segment 的生命周期经历两个阶段[新写入数据流] | v [ Growing Segment ] (纯内存写入缓冲支持暴力搜索不建索引) | 达到 512MB / 定时触发 Flush v [ Sealed Segment ] (不可变数据段落盘持久化至 MinIO/S3后台异步构建 HNSW 索引) | v 后台异步触发 [ Compaction 合并 ] (将多个小 Segment 或清理已删除数据的 Segment 合并为大 Segment)Growing Segment生长中段新插入Insert的向量数据首先进入 Growing Segment。它直接驻留在内存中为了保证写入速度它不构建复杂的 HNSW 图索引而是采用内存暴力搜索Brute-force Scan。Sealed Segment封存段当 Growing Segment 的大小达到阈值默认 512 MB或应用显式调用了collection.flush()时该段会被封存为不可变Immutable的 Sealed Segment 并落盘存储到对象存储MinIO/S3。随后IndexNode 节点会拉取该段数据为其构建 HNSW 或 IVF 索引。为什么 Compaction 会引发查询抖动随着业务频繁地进行数据插入Insert、更新Upsert和删除Delete系统内会产生大量包含墓碑标记Delete Bitset的碎片化小 Segment。为了提升检索效率和回收磁盘空间Milvus 的 DataCoord 节点会定期调度后台Compaction压缩合并任务磁盘与网络 I/O 巨额争抢Compaction 任务需要从对象存储拉取 5~10 个小 Segment在 DataNode 节点上读取全量向量数据、应用 Delete 位图剔除已删除数据重新重组为一个完整的 512 MB 大 Segment并重新上传对象存储。CPU 与内存带宽打满合并完成后IndexNode 需要为这个新合并出的大 Segment重新全量构建 HNSW 索引建图过程中的多线程高维向量距离计算会瞬间吃满物理机的 CPU 核心与内存总线。QueryNode 热替换时的瞬时卡顿新索引建好后QueryNode 必须将新 Segment 加载进内存并将旧的几个小 Segment 从内存中卸载Release。在原子切换Atomic Swap的短暂瞬间查询线程需要获取读写锁导致正在并发执行的在线检索请求发生毫秒级的排队等待。彻底根除查询抖动的四大生产实操策略1. 严禁在线业务代码中高频调用collection.flush()很多初学者写完一段插入代码后顺手就写一句collection.flush()。这会导致每次只插入几百条数据就强行封存一个微小的 Segment系统在几分钟内产生上百个碎片 Segment直接逼迫后台 Compaction 频繁满负荷运转。最佳做法依赖 Milvus 自身的后台自动 Flush 机制默认每秒批量提交或者只在离线大批量灌库结束后显式调用一次flush()。2. 在物理拓扑上实现读写与计算节点隔离在 Kubernetes 生产部署中通过节点亲和性Node Affinity和资源隔离将负责在线查询的QueryNode与负责建索引/合并的IndexNode / DataNode分离部署在不同的物理机上# Kubernetes Pod 亲和性隔离配置示例 spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-role.kubernetes.io/milvus-query operator: In values: [true]这样无论 IndexNode 在后台合并时 CPU 烧到多少QueryNode 所在的机器 CPU 和内存总线始终保持 100% 清爽绝不影响在线查询。3. 调优 Compaction 触发阈值与执行时间窗口在milvus.yaml中合理配置 Compaction 参数避免在白天业务高峰期频繁合并dataCoord: compaction: enable: true # 限制单次合并的最大 Segment 数量降低单次任务的瞬时压力 maxSegmentRun: 10 # 设定最小 Segment 尺寸避免微小合并 minSegmentSizeToCompact: 268435456 # 256 MB4. 控制并发 Compaction 任务数限制 DataNode 与 IndexNode 允许并发执行的 Compaction 线程数如compaction.maxParallelTask: 2防止合并任务占用全部 CPU 时间片。总结了解了 Segment 从 Growing、Sealed 到 Compaction 的生命周期你就掌握了 Milvus 性能调优的底牌管住业务端的滥用 Flush在基础设施层拆分 Query 与 Index 物理资源微调后台合并阈值。三管齐下就能彻底抹平周期性性能毛刺让向量检索服务的 P99 曲线平稳如镜。