Quota模块
etcdserver:mvcc:database space exceeded错误:
- 默认db配额仅为2G
- etcd v3 是个MVCC数据库,保存了key的历史版本
etcd server收到put/txn等写请求的时候,会首先检查当前etcd db大小加上请求的key-value大小只是否超过了配额。
如果超过配额,会产生一个告警请求,告警类型为NO SPACE,并通过Raft日志同步给其他节点,告知db无空间,并将告警持久化
到db中。
etcd设置建议配额不超过8G。APPLY模块在执行每个命令的时候,都会去检查当前是否存在NO SPACE告警,如果有则拒绝写入。
在调大配额之后,需要发送一个取消告警的命令,以消除所有告警。
检查etcd的压缩是否开启、配置是否合理。在配置etcd db配额,就不要设置小于0的,这样是禁用配额功能。
KVServer模块
Preflight Check
为了保证集群稳定性,避免雪崩,任何提交到raft模块的请求,都会做一些简单的限速判断。
- 如果Raft模块以提交的日志索引比已应用到状态机的日志索引超过5000,就会返回一个etcdserver: too many requests错误给client。
- 如果token无效,返回一个auth:invalid auth token错误给client。
- 如果写入的包大小超过默认的1.5MB,就会返回etcdserver:request is too large的错误给client。
Propose
在经过检查之后,会生成一个唯一的ID,将此请求关联到一个对应的消息通知channel,然后raft模块发起Propose一个提案,向raft模块发起的提案后,
KVServer模块会等待此put请求,等待写入结果通过消息通知channel返回或者超时。etcd默认超时时间是7秒,如果一个请求超时未返回结果,则可能会出现你熟悉的
WAL模块
Raft模块收到提案后,如果当前节点是Follower,它会转发给Leader,只有Leader才能处理写请求,Leader收到提案后,通过Raft模块输出待转发给Follower
节点的消息和待持久化的日志条目。
etcdserver 从Raft模块获取到以上消息和日志条目后,作为Leader,它会将put提案消息广播给集群各个节点,同时需要把集群Leader任期号、投票信息、
以提交索引、提案内容持久化到一个WAL日志文件中,用于保证集群的一致性,可恢复性。
WAL记录是按照顺序追加写入组成,每个记录由类型(Type)、数据(Data)、循环冗余校验码(CRC)组成。
WAL记录类型目前支持5种,分别是文件元数据记录、日志条目记录、状态信息记录、CRC记录、快照记录:
- 文件元数据记录包含节点ID、集群ID信息,它在WAL文件创建的时候写入;
- 日志条目记录包含Raft日志信息,如put提案内容;
- 状态信息记录,包含集群的任期号、节点投票信息等,一个日志文件种会有多条,以最后的记录为准;
- CRC记录包含上一个WAL文件最后的CRC信息,用于校验数据文件的完整性、准确性等;
- 快照记录包含快照的任期号、日志索引信息,用于检查快照文件的准确性。
WAL模块是如何持久化一个put提案的日志条目记录:
- Raft日志条目的数据结构:Term是Leader任期号,随着Leader选举增加;Index是日志条目的索引,单调递增增加;Type是日志类型;Data是日志数据。
- 将Raft日志条目内容序列化到WAL记录的Data字段,然后计算Data的CRC值,设置Type为Entry Type。
- 计算WAL记录的长度,顺序先写入WAL长度,然后写入记录内容,调用fsync持久化到磁盘,完成将日志条目保存到持久化存储中。
- 当一半以上节点持久化此日志条目后,Raft模块就会通过channel告知etcdserver模块,put提案已经被集群多数节点确认,提案状态为已提交,
- etcdserver模块从channel取出提案内容,添加到先进先出调度队列,随后通过Apply模块按入队顺序,异步、一次执行提案内容。
每个提案被提交前都会被持久化到WAL文件中,以保证集群的一致性和可恢复性。
Apply模块
etcd的幂等性是根据Raft日志条目中的索引字段。etcd通过引入consistent index字段,来存储系统当前已经执行过的日志条目索引,实现幂等性。
Apply模块基于consistent index和事务实现了幂等性。
MVCC
MVCC主要是由两部分组成,一个是内存索引模块treeIndex,保存key的历史版本信息,另一个是boltdb模块,用来持久化存储key-value数据。
treeIndex
版本号在etcd里面发挥着重大作用,它是etcd的逻辑时钟。etcd启动的时候默认版本号是1,从最小值1开始枚举到最大值,未读到数据的数据则结束,最后读出来的
版本号即时当前etcd的最大版本号currentRevision。
boltdb
boltdb是一个基于B+tree实现的key-value嵌入式db,通过提供桶机制实现类似于MySQL表的逻辑隔离。
将修改的数据放入到一个名为key的桶里,在启动etcd时自动创建。
boltdb value的值是将包含key名称、key创建是时版本号、最后一次修改的版本号、修改菜蔬、value值、租赁信息序列化为二进制数据。
etcd使用合并再合并解决写性能差的问题:
- put/delete操作时,都会基于当前版本号递增生成新的版本号,因此属于顺序写入,可以递增boltdb的bucket.FillPercent参数,使每个page填充更多数据,
减少page的分裂次数并降低db空间。- etcd通过多次合并多个写事物请求,通常情况下,是异步机制定时(默认每隔100ms)将批量事务一次性提交,从而大大提高吞吐量,这样,读请求可能无法从
boltdb获取到最新数据。- etcd bucket buffer来保存暂未提交的事务数据。在更新boltdb的时候,etcd也会同步数据到bucket buffer。
etcd在执行读请求过程中设置磁盘IO吗
etcd在启动时候通过mmap机制将etcd db文件映射到etcd进程地址空间,并设置mmap的MAP_POPULATE flag,它会告诉Linux内核预读文件,Linux就会将文件内容
拷贝到物理内存中,此时会产生磁盘I/O。节点在内存足够的请求下,后续处理读请求过程中就不会产生磁盘I/O了。
如果etcd节点内存不足时,可能会导致db文件对应的内存页被换出,当读请求命中的文件未在内存中时,就会产生缺页异常,导致读过程中产生磁盘IO,
可以通过观察etcd进程
可以通过观察etcd进程的majflt字段来判断etcd是否产生了主缺页中断。
网友评论