1. etcd 存储架构概述:理解底层存储引擎、MVCC 机制与多版本数据保留策略
在深入 etcd 的数据压缩与碎片整理之前,必须首先理解其底层存储架构。etcd 的存储层设计直接决定了数据膨胀、版本堆积以及空间回收的机制。本节将拆解 etcd 的三大核心存储要素:BoltDB/Go-bbolt 存储引擎、MVCC 多版本并发控制以及多版本数据保留策略。
1.1 底层存储引擎:BoltDB / Go-bbolt
etcd v3 版本之后,底层存储引擎从 Google 的 LevelDB 切换为 BoltDB(后由社区维护为 Go-bbolt)。BoltDB 是一个纯 Go 实现的嵌入式键值存储引擎,其核心特点如下:
| 特性 | 说明 |
|---|---|
| 存储模型 | 基于 B+ 树,所有数据存储在单个内存映射文件中(默认 member/snap/db) |
| 事务支持 | 支持 ACID 事务,提供读事务(快照隔离)和写事务(串行化) |
| 并发控制 | 同一时刻只允许一个读写事务,允许多个只读事务并行 |
| 数据组织 | 数据按 Bucket(桶)组织,每个 Bucket 内存储键值对 |
| 文件结构 | 单文件存储,通过 mmap 映射到进程地址空间,读写效率高 |
关键认知:BoltDB 的 B+ 树结构天然支持范围查询和顺序遍历,这与 etcd 的 watch 机制和前缀查询需求高度匹配。但 B+ 树的页分裂和页合并行为,也是后续碎片产生的根本原因。
etcd 在 BoltDB 之上构建了多个核心 Bucket,用于存储不同类别的数据:
- key:存储所有键值对的主数据,键为 revision(版本号),值为键值数据
- meta:存储元数据,如当前集群的 term、committed index 等
- members:存储集群成员信息
- auth:存储认证相关的角色和用户信息
1.2 MVCC 机制:多版本并发控制
etcd 实现了完整的 MVCC(Multi-Version Concurrency Control) 机制。每次对键值对的写入(Put)或删除(Delete)操作,都会生成一个新的版本记录,而不会原地覆盖旧数据。这是 etcd 支持历史查询、watch 事件回溯以及事务一致性的基础。
MVCC 核心设计:每个键值对关联一个全局递增的
revision(主版本号)。revision 由两部分组成:main(事务 ID)和 sub(事务内操作序号)。etcd 通过 revision 来区分不同版本的数据。
MVCC 的工作流程如下:
- 写入操作:客户端发起 Put(key, value) 请求
- 分配 revision:etcd 分配一个新的全局 revision,作为该版本的唯一标识
- 存储新版本:将
revision → key, value写入 BoltDB 的 key bucket - 更新索引:在内存 BTree 索引中记录 key 到最新 revision 的映射
- 保留旧版本:旧版本数据仍然保留在 BoltDB 中,不会被立即删除
以下 SVG 图展示了 etcd 的 MVCC 多版本存储结构:
1.3 多版本数据保留策略
MVCC 机制虽然带来了强大的功能,但也导致了存储空间的持续增长。每次更新或删除操作都会产生新的版本,旧版本数据并不会自动清理。etcd 通过以下机制管理多版本数据的保留与回收:
| 机制 | 触发条件 | 行为 | 影响 |
|---|---|---|---|
| 自动压缩(Auto Compaction) | 基于时间(如每 5 分钟)或版本数(如保留 10000 个 revision) | 标记旧版本为“可删除”,但不会立即回收磁盘空间 | 减少逻辑版本数,但 BoltDB 文件大小不变 |
| 手动压缩(Compact) | 运维人员执行 etcdctl compact 命令 |
指定一个 revision,删除该 revision 之前的所有历史版本 | 释放逻辑空间,但物理空间仍被占用 |
| 碎片整理(Defrag) | 运维人员执行 etcdctl defrag 命令 |
重新整理 BoltDB 文件,回收空闲页 | 真正释放磁盘空间,缩小 db 文件 |
重要区分:压缩(Compact)和碎片整理(Defrag)是两个不同的操作。压缩只标记旧版本为可删除,不回收磁盘空间;碎片整理才真正回收物理空间。很多运维人员混淆这两个概念,导致压缩后磁盘空间并未减少。
以下 SVG 图展示了 etcd 数据从写入到压缩再到碎片整理的完整生命周期:
1.4 存储空间膨胀的根本原因
综合以上架构特点,etcd 存储空间膨胀主要源于以下三个因素:
- MVCC 版本堆积:每次写入都生成新版本,旧版本未被及时清理,导致 BoltDB 中存储了大量冗余数据
- B+ 树页分裂:频繁的插入和更新操作导致 B+ 树节点分裂,产生大量半满或几乎为空的页,形成内部碎片
- 压缩不彻底:压缩操作仅标记旧版本为可删除,但 BoltDB 不会自动合并空闲页,空闲页仍占用磁盘空间
运维启示:理解 etcd 的存储架构是制定压缩和碎片整理策略的前提。只有掌握了 MVCC 的多版本特性、BoltDB 的页管理机制以及压缩与碎片整理的区别,才能在实际运维中有效控制 etcd 的存储空间,避免磁盘爆满导致集群不可用。