第 9 篇 说明
CompactRevision →
ErrCompacted;第 7 篇 提过
quota 门。运维里常见混淆是 compaction 与 defrag
当一件事,或 quota 触顶先
defrag——实际上:compaction 删历史 revision;defrag
回收 bbolt 文件空洞;quota 看 Backend().Size()
与 alarm 写禁。
本篇钉 v3.5.33
容量轴三件套:Compact/scheduleCompaction、v3compactor
auto-compaction、Defrag 在线重组,以及
DefaultQuotaBytes = 2GiB 与
NOSPACE
alarm。不写未测磁盘曲线。
本篇在系列中的位置
篇目 核心内容 第 11 篇 · Txn 与并发语义 compare/mod、Jepsen 边界 第 12 篇 · Compaction、Defrag 与容量 compaction、defrag、quota、SLO 第 13 篇 · Kubernetes 控制面耦合 resourceVersion、Node Lease 系列目录 五轴、阅读路径
版本锚定:etcd v3.5.33(tag
v3.5.33)。对齐server/mvcc/kvstore_compaction.go、server/mvcc/backend/backend.go(Defrag)、server/etcdserver/quota.go、server/etcdserver/api/v3compactor/@ etcd-io/etcd v3.5.33。默认--quota-backend-bytes=0表示启用 2GB quota(非「无限制」)。无真实集群则不粘贴伪造endpoint statusdbSize 数字。
一、Compaction:裁 revision,不缩文件
Manual
compact:Compact(rev) 经 Raft propose
→ store.compact →
scheduleCompaction:
treeIndex.Compact(rev)更新内存索引,返回需保留的 revision 集合keep;- 分批
UnsafeRange+UnsafeDelete清理buckets.Key中< rev且不在keep的条目; - 写入
finishedCompactKeyName,compactMainRev = rev。
Compaction 降低 MVCC 历史深度,使 Watch
从旧 startRev 追赶时可能命中
minRev < compactMainRev(第
9 篇)。Range 指定 Rev < compactMainRev 返回
ErrCompacted。
未完成 compaction
恢复:scheduledCompactKeyName 与
Restore
路径可在重启后继续(kvstore.go)——排障「长时间
compacting」应查 scheduled vs finished meta
key,而不是直接 defrag。
flowchart LR
compact["Compact(rev)"] --> index["treeIndex.Compact"]
index --> bolt["bbolt 删旧 revision KV"]
bolt --> meta["finishedCompact rev"]
meta --> watch["unsynced: CompactRevision"]
meta --> range["Range@oldRev: ErrCompacted"]
二、Auto-compaction:保留窗口 vs 恢复能力
EtcdServer 在
AutoCompactionRetention != 0 时构造
v3compactor(periodic.go /
revision.go):
- periodic 模式:按 wall-clock 保留时长(如 1h)滑动窗口;每 period/10 采样 revision,保留期满 compact 最早采样 rev;
- revision 模式:保留最近 N 个 main revision。
Leader 失去 leadership 时
compactor.Pause(),重新 Leader 时
Resume()(server.go
updateLeadership)——避免 Follower 上错误触发
compact。
SLO 写法(工程判断,非官方 SLA):
| 指标 | 依赖 | 风险 |
|---|---|---|
| Watch 可续传窗口 | ≥ 客户端最大断线时间 + apiserver resync | 保留过短 → ErrCompacted 风暴 |
| 磁盘增长 | 写入率 × 保留 revision 宽度 | 只 compact 不 defrag → SizeInUse ≪ Size |
| 恢复 / 备份 | 最近 compact rev 之后仍可做增量语义 | 过 aggressive compact 丢 historical read |
K8s 官方文档建议控制 db size 与 defrag 窗口(B 级运维指南);本篇不复制未核实的 GB 阈值表。
三、Defrag:缩 bbolt 文件,不替 compaction
backend.Defrag()(backend.go):
- 持
batchTx+mu+readTx锁,阻塞并发读写; - 新建临时 bolt 文件,拷贝 live page;
- 替换原 db 路径。
Defrag 不删除 MVCC 历史——只把 compaction
后的空洞从文件物理移除。日志字段
current-db-size vs
current-db-size-in-use
差值大时,defrag 才有明显效果。
Prometheus
etcd_mvcc_db_total_size_in_use_bytes
/
etcd_mvcc_db_total_size_bytes
与 isDefragActive gauge
可用于变更窗口观测;本篇无实测 shrink
比例。
运维口令:先 compact 到目标 rev → 低峰 defrag 单 member(避免全集群同时锁 store);与 snapshot 备份窗口错开(第 14 篇)。
四、Quota 与 NOSPACE alarm
DefaultQuotaBytes = 2 * 1024 * 1024 * 1024(2GiB);MaxQuotaBytes = 8GiB(超过仅
warn,仍允许配置)。
backendQuota.Available:Backend().Size() + Cost(req) < maxBackendBytes。Cost
估算:
- Put:
256 + len(key) + len(value)(kvOverhead); - Txn:Success/Failure 分支 较大 一侧的 Put 成本之和;
- LeaseGrant:
64(leaseOverhead)。
Apply 返回 ErrNoSpace
时(server.go
applyEntryNormal):
- 若尚无
NOSPACEalarm → 异步Alarm ACTIVATE NOSPACE; applierV3Capped拒绝后续 mutating(只读仍可部分进行——见apply.goalarm 分支)。
Disarm:compact + defrag 降
Size() 后
etcdctl alarm disarm(需理解
10% mmap
余量:QuotaBackendBytes + QuotaBackendBytes/10
用于 MmapSize,backend.go)。
| 现象 | 轴 | 首要动作 |
|---|---|---|
database space exceeded |
MVCC/quota | compact → defrag → 升 quota(≤8G 慎) |
| 写拒绝、alarm NOSPACE | alarm | disarm 前必须 真正腾出 Size |
| 读 old rev 失败 | compaction | 客户端全量 sync,非 defrag |
| dbSize 大、in-use 小 | 空洞 | defrag |
CORRUPT alarm 切换
applierV3Corrupt(只读防护)——与容量轴不同,见
corrupt checker(第 15 篇)。
五、与 Watch / K8s 的 SLO 耦合
- auto-compaction-retention
过短:controller/apiserver Watch 从旧
resourceVersion 续传失败 → 与 第 9 篇
ErrCompacted同一机制; - quota 触顶:写 Node Lease、Status 失败 → 表象像 Lease/TTL 轴(第 10 篇),根因常在 MVCC 轴;
- defrag 停顿:短窗口内 ReadIndex/apply 延迟上升——与 Raft lag 分列(apply 等锁,非复制慢)。
建议 SLO 文档显式写三项:compaction 保留 ≥ 最大
watch 落后时间;quota 告警在 80%
Size;defrag 变更单 per
member。数字阈值须来自 本集群
metrics + 变更记录,非本文臆测。
六、谱系与开放问题
MVCC 时间旅行(多版本)→ 历史必须裁剪(compaction)
→ bbolt 追加写 → 空洞 → defrag
→ 单 store quota → Multi-Raft 扩展性天花板(TiKV 对照)
→ 开放:K8s 大规模下 auto-compaction 与 apiserver bookmark 的联合调参无公开形式模型
开放问题:2GiB 默认 quota 对 modern 控制面是否偏小属于 workload 依赖;迁 TiKV/FDB 的否证条件见 distributed/39 与第 16 篇——不在本篇给 QPS 排行。
本篇不写什么:未跑的 defrag 耗时
benchmark;etcdctl check perf
伪造输出;TiKV/FDB 内核重讲。
参考资料
规范 / 官方文档 / 源码(A)
- etcd-io/etcd
v3.5.33:server/mvcc/kvstore_compaction.go;server/mvcc/kvstore.go(Compact);server/mvcc/backend/backend.go(Defrag);server/etcdserver/quota.go;server/etcdserver/api/v3compactor/periodic.go;server/etcdserver/server.go(NOSPACE alarm) - etcd v3.5 · Maintenance / Compaction / Defrag
- etcd
v3.5 ·
--quota-backend-bytes - etcd v3.5 · Alarms
运维指南(B)
- etcd Hardware guidelines / Performance(容量与磁盘指针,非 benchmark 结论)
站内对照
上一篇:Txn 与并发语义
下一篇:Kubernetes 控制面耦合
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【etcd】生产全景:缺口、五轴坐标系与 16 篇路线
相对 distributed/50、39、13 钉清 etcd 生产内核缺口;定义 Raft/WAL/MVCC/Watch/Lease 五轴排障坐标系,给出 16 篇阅读路线与 K8s 控制面耦合指针。版本锚定 v3.5.33。
【etcd】MVCC 数据模型:Revision、keyIndex 与 generation
拆解 etcd v3.5.33 的 Revision (main, sub) 全序、keyIndex/generation 生命周期与 treeIndex 分工;简要对照 v2 平面键空间,交代 MVCC 与 Watch/compaction 的语义基础。
【etcd】bbolt 后端:mmap、batch 与单写者约束
钉清 etcd v3.5.33 的 bbolt 后端:bucket 布局、mmap 预映射、100ms/10000 条 batch commit、ConcurrentReadTx 与单写者如何约束 MVCC 读写并发。
【etcd】写入路径深读:Txn、mod revision 与 quota/alarm
走读 etcd v3.5.33 写入路径:raftRequest 背压、Txn compare/mod 与 ModRevision 分配、backend quota 与 NOSPACE alarm 如何把集群推入只读。