读写处理 集群正常提供服务时Member会处于Leader,Follower两种状态。
为了保证数据的强一致性,etcd集群中所有的数据流向都是一个方向,从Leader流向 Follower,也就是所有Follower的数据必须与Leader保持一致,如果不一致会被覆盖。
Leader可以响应客户端的读操作和写操作,Follower可以直接响应客户端的读操作。读操作不改变数据且所有实例的数据一致。但写操作会统一被转发到Leader进行处理。Leader需要等待其他Member实例确认这次写操作,只有超过半数的实例确认写操作才可以认为这次写操作成功。
etcd采用了wal预写式日志,即write ahead log。所有的数据库事务会先以日志的形式记录下来,再由其他进程负责写入持久稳定的db。etcd的写操作等待过半实例确认的过程就是等待过半实例已经将事务写入日志的过程。
快照为了配合wal,etcd会定期生成数据库的快照snapshot。快照记录了某一时间节点的etcd数据库数据,这样wal就只需要保留该时间之后的所有日志,防止日志体积随时间的积累和过于庞大。快照也可以用于数据恢复,故障的实例只需要凭借快照并相应的wal日志执行操作就可以很快的同步数据。
$ tree /data/etcd/default.etcd/member/
/data/etcd/default.etcd/member/
├── snap
│ └── db
└── wal
├── 0000000000000000-0000000000000000.wal
└── 0.tmp2 directories, 3 files
etcd 数据空间管理
etcd 的数据存储的方式,是直接通过 mmap 一个文件到内存中,然后构建 B+树 的方式。如果有数据删除,相关使用的存储空间也不会释放,可能会在其中留下很多空洞。
压缩 etcd 空间也可以减少 etcd 程序的内存占用量,提高 etcd 性能,在没有问题的时候提前进行压缩也是明智的选择
碎片数据统计
具体的空洞文件的大小,可以根据相关的 metric 计算出来,一旦达到了一定阈值,就可以做一次碎片整理。具体计算方式为:
curl -s http://localhost:2379/metrics |grep -iE "^etcd_mvcc_db_total_size_in_bytes|^etcd_mvcc_db_total_size_in_use_in_bytes"
etcd_mvcc_db_total_size_in_bytes 20480
etcd_mvcc_db_total_size_in_use_in_bytes 16384
- etcd_mvcc_db_total_size_in_bytes: etcd的磁盘存储总共占用的空间
- etcd_mvcc_db_total_size_in_use_in_bytes: 实际用于数据存储占用的空间
- etcd_mvcc_db_total_size_in_bytes - etcd_mvcc_db_total_size_in_use_in_bytes: 通过碎片整理可以释放出来的空间
历史压缩
因为 etcd 保持它的键空间的确切历史,这个历史应该定期压缩来避免性能下降和最终的存储空间枯竭。压缩键空间历史删除所有关于被废弃的在给定键空间修订版本之前的键的信息。这些key使用的空间随机变得可用来继续写入键空间。
键空间可以使用 etcd
的时间窗口历史保持策略自动压缩,或者使用 etcdctl
手工压缩。 etcdctl
方法在压缩过程上提供细粒度的控制,反之自动压缩适合仅仅需要一定时间长度的键历史的应用。
etcd
可以使用带有小时时间单位的 --auto-compaction
选项来设置为自动压缩键空间:
# 保持一个小时的历史
$ etcd --auto-compaction-retention=1
根据实践发现只配置auto-compaction-retention只会做碎片整理,不会实际减少空间大小; 如果需要减少大小还是需要使用etcdctl compact 和 etcdctl defrag清理空间
etcdctl
如下发起压缩工作:
- 先找到最新的revision
$ rev=$(ETCDCTL_API=3 etcdctl --endpoints=:2379 endpoint status --write-out="json" | egrep -o '"revision":[0-9]*' | egrep -o '[0-9]*')
- 进行一次压缩,释放掉老的版本
$ ETCDCTL_API=3 etcdctl compact $rev compacted revision 1516
在压缩修订版本之前的修订版本变得无法访问:
$ etcdctl get --rev=2 somekey
Error: rpc error: code = 11 desc = etcdserver: mvcc: required revision has been compacted
反碎片化
在压缩键空间之后,后端数据库可能出现内部。内部碎片是指可以被后端使用但是依然消耗存储空间的空间。反碎片化过程释放这个存储空间到文件系统。反碎片化在每个成员上发起,因此集群范围的延迟尖峰(latency spike)可能可以避免。
需要注意的是,在整个碎片整理过程中,整个member将处于不可用状态,因此针对多点部署的etcd,在执行碎片整理的时候,要逐台进行整理,并且在整理过程中保证其他节点能够满足quorum需求。
通过留下间隔在后端数据库,压缩旧有修订版本会内部碎片化 etcd
。碎片化的空间可以被 etcd
使用,但是对于主机文件系统不可用。
为了反碎片化 etcd 成员, 使用 etcdctl defrag
命令:
$ etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
- 测试是否恢复
$ ETCDCTL_API=3 etcdctl put newkey 123
OK
etcd 服务启动配置实践
1 heartbeat timeout 默认为 100ms,推荐配置为 1s;
2 election timeout 默认为 1000ms,推荐为 5s(election timeout >= 5 * heartbeat timeout);
3 quota-backend-bytes 默认为 2G(最大值8G),推荐根据集群容量预估调整;
4 配置 auto-compaction-retention=1 和 auto-compaction-mode=periodic 参数,定期压缩历史数据;
5 推荐通过 cronjob 定期执行 etcdctl defrag(如果 defrag 执行时间 > election timeout,则集群会进入重新选主模式)