Milvus 集合级自动 Compaction 开关:collection.autocompaction.enabled 设计与实现全解析
本技术指南围绕 Milvus 中"集合级(Collection Level)自动 Compaction 开关"这一能力展开,其源头是设计提案 20230511-collection_level_autocompaction_switch.md(对应 Enhancement Issue #23993)。阅读本文后,你将掌握:全局自动 compaction 与集合级属性的优先级解析规则、该特性在 DataCoord 中的底层实现链路、如何通过 AlterCollection API 与各语言 SDK 动态开启/关闭单个集合的自动 compaction,以及仓库中对应的单元测试与端到端验证方式。文章以该 MEP 为骨架,结合仓库源码、配置与测试逐层印证,帮助你在生产环境中精确控制 compaction 行为。
背景与动机:为什么需要集合级别的自动 Compaction 开关
全局开关的粒度局限
Milvus 的 DataCoord 负责发现并调度 compaction。历史上,是否启用"自动 compaction"只由一个全局配置控制,即 configs/milvus.yaml 中位于 DataCoord 段下的两个开关(见 configs/milvus.yaml):
dataCoord:
enableCompaction: true
compaction:
# Switch value to control if to enable automatic segment compaction during which
# data coord locates and merges compactable segments in the background.
# This configuration takes effect only when dataCoord.enableCompaction is set as true.
enableAutoCompaction: true
从配置注释可知两者语义不同:dataCoord.enableCompaction 是 compaction 功能的总开关(Compaction 可将小 segment 合并成大 segment,并清理超出 Time Travel 保留期的删除实体);而 dataCoord.compaction.enableAutoCompaction 控制 DataCoord 是否在后台自动定位并合并可压缩的 segment,且前者必须为 true 后者才生效。
该全局开关作用于系统中所有集合,无法区分不同集合的业务诉求。这正是设计文档 Summary 中描述的痛点。
典型业务场景
设计文档给出了两个需要"按集合细粒度控制"的典型场景:
- 数据导入期间禁止自动 compaction:批量导入(bulk insert / import)时若后台自动 compaction 持续触发,会导致 segment 反复重建、索引频繁重构建,拖慢导入吞吐并产生不必要的写放大;
- 测试期间保证行为稳定:某些自动化测试或基准测试希望系统行为可预期,需要临时关闭个别集合的自动 compaction,而不是改动全局配置去影响整个集群中的其他集合。
一句话概括:把"自动 compaction 是否开启"从系统级全局变量下沉为可逐集合覆盖的元数据属性。
设计方案:一条集合属性与三级取值规则
MEP 的 Design 一节给出了简洁的落地方案:
新增集合级属性,属性键为
collection.autocompaction.enabled(该键在源码 pkg/common/common.go 中定义为常量CollectionAutoCompactionKey)。
在处理所有 compaction 信号时,按以下规则解析"该集合当前是否允许自动 compaction":
- 未设置:使用全局自动 compaction 设置;
- 取值合法:使用集合级设置;
- 取值非法:按设计文档意图应回退到全局设置(注意下方"实现层面的差异"小节会说明当前仓库代码的实际行为)。
将这条属性与同一代码块中其他集合级属性(如 collection.ttl.seconds、collection.on.truncating 以及各类限流与配额键)并列可见,Milvus 的"集合 properties"机制天然支持此类按集合覆盖系统行为的配置模式。
属性定义与 SDK 封装:从常量到客户端构造器
服务端常量定义
集合属性键集中定义在 pkg/common/common.go:
const (
CollectionTTLConfigKey = "collection.ttl.seconds"
CollectionAutoCompactionKey = "collection.autocompaction.enabled"
...
)
CollectionAutoCompactionKey = "collection.autocompaction.enabled" 即为贯穿全文的属性键名,服务端(DataCoord 解析)与客户端(Go/Python SDK 构造属性)共用同一字符串值。
Go SDK 中的集合属性构造器
在 Go SDK 侧,client/entity/collection_attr.go 提供了类型安全的构造器:
// cakAutoCompaction const for collection attribute key autom compaction enabled.
const cakAutoCompaction = `collection.autocompaction.enabled`
// CollectionAutoCompactionEnabled returns collection attribute to set collection auto compaction enabled.
func CollectionAutoCompactionEnabled(enabled bool) autoCompactionCollAttr {
ca := autoCompactionCollAttr{}
ca.key = cakAutoCompaction
ca.value = strconv.FormatBool(enabled)
return ca
}
CollectionAttribute 接口包含 KeyValue() (string, string) 与 Valid() error 两个方法,其中 Valid() 通过 strconv.ParseBool 校验值必须是合法布尔串(true/false),否则返回 auto compaction setting is not valid boolean 错误,在请求构造阶段即可拦截非法值。SDK 还支持 CollectionTTL 等其它集合属性,体现了同一套属性抽象。
Go Client 的 Alter 链路
Go 客户端将属性写入集合的入口是 AlterCollectionProperties,实现在 client/milvusclient/collection.go:
func (c *Client) AlterCollectionProperties(ctx context.Context, option AlterCollectionPropertiesOption, callOptions ...grpc.CallOption) error
配套的 option 构造器在 client/milvusclient/collection_options.go:
func NewAlterCollectionPropertiesOption(collection string) *alterCollectionPropertiesOption
func (opt *alterCollectionPropertiesOption) WithProperty(key string, value any) *alterCollectionPropertiesOption
option 内部将属性收集到 map 后,经 entity.MapKvPairs 转换为 commonpb.KeyValuePair,再封装为 milvuspb.AlterCollectionRequest 发送给服务端——这就是设计文档中"所有集合级属性均可通过 AlterCollection API 修改"这一结论的 Go 落地路径。
实现原理:DataCoord 如何消费集合级开关
全局触发循环的启动条件
在 DataCoord 中,自动 compaction 的全局心跳循环位于 internal/datacoord/compaction_trigger.go 的 schedule():
// If AutoCompaction disabled, global loop will not start
if !Params.DataCoordCfg.EnableAutoCompaction.GetAsBool() {
return
}
即:只有当全局 EnableAutoCompaction 为真时,后台周期性的全局 compaction 信号才会产生。这是第一道"全局闸门"。
compaction 信号的入口校验
TriggerCompaction(internal/datacoord/compaction_trigger.go)在分配信号 ID 前还会做一次快速判断:
// If AutoCompaction disabled, flush request will not trigger compaction
if !paramtable.Get().DataCoordCfg.EnableAutoCompaction.GetAsBool() && !paramtable.Get().DataCoordCfg.EnableCompaction.GetAsBool() {
return -1, nil
}
只有两个全局配置均为 false 时才会直接放弃(此时 flush 也不再触发自动 compaction);只要 compaction 功能总开关或自动 compaction 任一打开,信号即可进入后续处理。
handleSignal 中的集合级过滤
真正消费集合级开关的位置在 handleSignal(internal/datacoord/compaction_trigger.go):
if !signal.isForce && !isCollectionAutoCompactionEnabled(coll) {
log.RatedInfo(context.TODO(), rate.Limit(20), "collection auto compaction disabled")
return nil
}
关键点有两个:
- 过滤发生在每个候选集合分组内——全局信号会遍历所有集合,但每个集合都会单独套用自身的属性判断,天然实现"按集合隔离";
isForce(手动/强制 compaction 信号)不受集合级开关约束。强制信号走独立的manualSignals通道(更高优先级),因此运维需要立即压缩时仍可绕过该开关。
属性解析函数与三级规则
集合级开关的核心解析函数在 internal/datacoord/util.go:
// getCollectionAutoCompactionEnabled returns whether auto compaction for collection is enabled.
// if not set, returns global auto compaction config.
func getCollectionAutoCompactionEnabled(properties map[string]string) (bool, error) {
// when collection is on truncating, disable auto compaction.
if _, ok := properties[common.CollectionOnTruncatingKey]; ok {
return false, nil
}
v, ok := properties[common.CollectionAutoCompactionKey]
if ok {
enabled, err := strconv.ParseBool(v)
if err != nil {
return false, err
}
return enabled, nil
}
return Params.DataCoordCfg.EnableAutoCompaction.GetAsBool(), nil
}
这段代码精确实现了设计文档的三级解析,还额外叠加了两条规则:
| 情形 | 解析结果 |
|---|---|
集合处于 truncating(存在 collection.on.truncating 属性) |
一律返回 false(关闭自动 compaction),且不报错 |
设置了 collection.autocompaction.enabled 且可被 ParseBool 解析 |
返回集合级布尔值(最高优先级) |
设置了该属性但值非法(如 "bad_value") |
返回解析错误 |
| 未设置该属性 | 回退到全局 Params.DataCoordCfg.EnableAutoCompaction |
调用方对错误与外部集合的处理
解析错误的最终兜底在 internal/datacoord/compaction_trigger.go 的 isCollectionAutoCompactionEnabled:
func isCollectionAutoCompactionEnabled(coll *collectionInfo) bool {
if coll == nil {
return false
}
if coll.IsExternal() {
mlog.Debug(context.TODO(), "collection auto compaction disabled for external collection", mlog.FieldCollectionID(coll.ID))
return false
}
enabled, err := getCollectionAutoCompactionEnabled(coll.Properties)
if err != nil {
mlog.Warn(context.TODO(), "collection properties auto compaction not valid, returning false", mlog.Err(err))
return false
}
return enabled
}
这里补充了两条约束:nil 集合与外部集合(external collection,如基于文件系统挂载的数据源)一律不做自动 compaction。
实现层面与 MEP 文案的差异说明
设计文档在"取值非法"情形下写的意图是"回退到全局设置",而当前仓库实际实现中,当值无法被 ParseBool 解析时,isCollectionAutoCompactionEnabled 会记录 WARN 日志并返回 false,即对该集合按"自动 compaction 关闭"处理,而非回退到全局值。从行为安全角度看,非法值按关闭处理属于 fail-closed 倾向,但若你依赖设计文档的"回退"语义,需要留意当前代码的实际表现——建议始终通过 SDK 传入 "true"/"false" 这类合法布尔值。
实操:如何修改集合级自动 Compaction 开关
方式一:Go SDK(官方 Go Client)
先构造集合属性,再调用 AlterCollectionProperties:
package main
import (
"context"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
func main() {
ctx := context.Background()
c, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
})
if err != nil {
panic(err)
}
defer c.Close(ctx)
// 关闭名为 "docs" 的集合的自动 compaction
err = c.AlterCollectionProperties(ctx,
milvusclient.NewAlterCollectionPropertiesOption("docs").
WithProperty(entity.CollectionAutoCompactionEnabled(false).KeyValue()))
if err != nil {
panic(err)
}
// 需要恢复时再次置为 true
err = c.AlterCollectionProperties(ctx,
milvusclient.NewAlterCollectionPropertiesOption("docs").
WithProperty(entity.CollectionAutoCompactionEnabled(true).KeyValue()))
if err != nil {
panic(err)
}
}
说明:
entity.CollectionAutoCompactionEnabled(b)返回的属性对象满足CollectionAttribute接口,其KeyValue()返回("collection.autocompaction.enabled", "true"/"false");也可直接WithProperty("collection.autocompaction.enabled", false)。若希望移除该属性让其重新跟随全局配置,可使用milvusclient.NewDropCollectionPropertiesOption(collectionName, "collection.autocompaction.enabled")。
方式二:Python SDK
仓库中的测试代码给出了 Python 侧的用法范式,例如 tests/python_client/cdc/testcases/test_collection_properties.py 通过 properties={"collection.autocompaction.enabled": "true"} 修改属性,并轮询 describe_collection 返回的 properties 直到其同步为期望值。核心形态如下:
from pymilvus import MilvusClient
client = MilvusClient(uri="http://localhost:19530")
# 关闭集合的自动 compaction
client.alter_collection_properties(
collection_name="docs",
properties={"collection.autocompaction.enabled": "false"},
)
# 查看属性是否生效
props = client.describe_collection("docs").get("properties", {})
print(props.get("collection.autocompaction.enabled")) # "false"
方式三:RESTful / 服务端 grpc
对不依赖 SDK 的场景,直接向 /v2/vectordb/collections/alter_properties 发送属性即可,请求载荷中的 properties 同样是 {"collection.autocompaction.enabled": "false"}(详见仓库 tests/restful_client_v2 中对集合属性修改的相关用例)。
与全局开关、强制 Compaction 的组合行为速查
以下矩阵汇总了"全局自动 compaction 开关 × 集合级属性"对一次非强制(自动)信号是否执行的影响:
全局 enableAutoCompaction |
集合属性 collection.autocompaction.enabled |
集合自动 compaction 是否执行 |
|---|---|---|
| true | 未设置 | 执行(回退全局=true) |
| true | "true" |
执行 |
| true | "false" |
不执行 |
| true | 非法值(如 "bad") |
不执行,打 WARN 日志(fail-closed) |
| false | 任意 | 全局心跳循环不启动,自动信号不会产生 |
| 任意 | 集合处于 truncating / 外部集合 | 不执行 |
| 任意(manual signal) | "false" |
执行(isForce 绕过集合级过滤,但受 TriggerCompaction 的全局总开关约束) |
测试计划与仓库验证证据
单元测试:覆盖开关的每个分支
该特性配套的单元测试集中在 internal/datacoord/compaction_trigger_test.go,其子测试与设计文档 Test Plan 一一对应:
collectionAutoCompactionConfigError(compaction_trigger_test.go):将属性值设为"bad_value",断言解析失败后不会进入 plan 生成(inspector.enqueueCompaction 不被调用);collectionAutoCompactionDisabled(compaction_trigger_test.go):属性值"false"时自动 compaction 信号被跳过;collectionAutoCompactionDisabled_force(compaction_trigger_test.go):同一"false"属性下,isForce=true的信号仍会执行 compaction(验证强制信号绕过集合级开关);TestIsCollectionAutoCompactionEnabledExternal(compaction_trigger_test.go):验证外部集合恒返回关闭。
另有针对 TestHandleSignal 中的全局开关(EnableAutoCompaction true/false 切换)测试,验证全局与集合两级开关的叠加关系。
E2E / 集成 / 同步测试
- Go 端到端用例(如 tests/go_client/testcases/snapshot_test.go)在快照相关场景中以
"collection.autocompaction.enabled": "false"构建集合,确保测试期间不被打断——这正是 MEP 动机中"让测试行为稳定"的直接应用; - Python 测试 tests/python_client/cdc/testcases/test_collection_properties.py 通过 CDC 双向校验该属性在上下游间的同步,并配合
describe_collection断言属性值与字符串大小写处理。
这些测试共同构成了设计文档 Test Plan 中 Unit / E2E / Integration 三个层级的落地实现,可作为读者参照复现的"可运行样例"。
最佳实践与注意事项
- 导入/灌库窗口关闭,之后恢复:大批量导入前
alter_collection_properties将该集合置为false,导入完成并flush后手动触发一次 compaction 或恢复true,即可避免导入期间反复重建索引(对应设计文档的首要动机); - 尽量用合法布尔值:属性值最终经过
strconv.ParseBool(Go)解析,"1"/"t"等宽松写法在 Go 的ParseBool中虽可能通过,但跨语言 SDK 与后续版本行为未必一致,统一使用"true"/"false"最稳妥; - 区分强制与自动:集合级开关只拦截自动信号;需要立即回收空间的运维操作应使用手动/强制 compaction,不要依赖"开启属性"来放行;
- 作用域意识:该属性只控制 compaction,与 TTL(
collection.ttl.seconds)、配额限流等其他集合属性彼此独立;且全局dataCoord.enableCompaction总开关仍然拥有最终否决权; - 验证生效:通过
describe_collection/DescribeCollection查看 properties 以确认修改已落盘生效(同步链路可参考上述 Python CDC 用例的轮询断言模式)。
小结与延伸阅读
collection.autocompaction.enabled 是 Milvus "集合属性(Collection Properties)"体系中一个小而典型的代表:一份 MEP 设计文档,加上服务端常量、SDK 构造器、DataCoord 触发链路上的三级解析,以及覆盖各分支的单测与 E2E,共同把"全局化、一刀切"的 compaction 控制下沉为"逐集合、可动态调整"的元数据开关。理解它的实现路径,也能举一反三地理解 TTL、磁盘配额、复制组等其他集合级属性的设计与接入方式。
相关资源:
- 设计提案原文:docs/design-docs/design_docs/20230511-collection_level_autocompaction_switch.md
- 属性键定义:pkg/common/common.go
- 全局配置项:configs/milvus.yaml
- DataCoord 触发与过滤逻辑:internal/datacoord/compaction_trigger.go、internal/datacoord/util.go
- 单元测试:internal/datacoord/compaction_trigger_test.go
- Go SDK 属性构造器与 Alter API:client/entity/collection_attr.go、client/milvusclient/collection.go、client/milvusclient/collection_options.go
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00