db大小会对那些方面造成影响:
- 启动耗时:etcd启动的时候,打开boltdb db文件,读取db文件所有key-value数据,用于重建内存treeIndex模块。大量key导致db文件过大的场景,导致etcd启动慢。
- 节点内存配置:通过mmap将db文件映射内存时,如果内存不足,出现却缺页文件中断,导致服务稳定性、性能下降。
- treeIndex性能:etcd不支持数据分片,内存中的treeIndex若保存了几十万到上千万的key,这回增加查询、修改操作的整体延时。
- boltdb性能:大db文件场景会导致事务提交耗时增长、抖动。
- 集群稳定性:大db文件场景下,一旦出现expensive request后,导致etcd OOM、节点带宽满而丢包。
- 快照:较大的db文件会导致Leader发送快照需要消耗较多的CPU、网络宽带资源,同时Follower节点重建还原慢。
treeIndex构建过程
treeIndex模块维护了用户key与boltdb key的映射关系,boltdb的key、value有包含了构建treeIndex的所需的数据,etcd启动的时候,会启动不同角色的
goroutine并完成treeIndex构建。
主goroutine
遍历boltdb获取所有key-value的数据,并将其反序列化成etcd的mvccpb.KeyValue结构。核心原理是基于etcd存储在boltdb中的key数据有序性,按版本号
从1开始批量遍历,每次查询1000条key-value记录,直到查询数据为空。
构建treeIndex索引的goroutine
从主goroutine获取mvccpb.KeyValue数据,基于key、版本号、是否带删除表示等信息,构建keyIndex对象,插入到treeIndex模块的B-tree中。
etcd启动时只有一个构建treeIndex的goroutine。
节点内存限制
etcd启动时,调用boltdb Open API的时候,设置了mmap的MAP_POPULATE flag,告诉内核预读文件,将db文件内容全部从磁盘加载到物理内存中
(通过mmap机制将db文件映射到内存中)。如果节点内存小于treeIndex内存之和,会触发缺页中断,导致读延时抖动、QPS下降。
boltdb事务提交的四个核心流程:B+ tree的重平衡、分裂,持久化dirty page、持久化freelist以及持久化meta data。
事务提交延时抖动的原因主要是在B+Tree树的重平衡和分裂过程中,需要从freeList中申请若干连续的page存储数据,或者释放空闲个的freelist。
freeList后端实现在boltdb中是array。如果db文件较大,又存在大量的碎片空闲页,很可能导致超时。同时事务提交过程中,也可能会释放若干个page给
freelist,因此需要合并到freelist的数组中,此操作时间复杂度是O(NlogN)。为了优化boltdb事务提交的性能,etcd实现了基于HashMap来管理freelist。
通过引入三个map数据结构(freemaps的key是连续的页数, value是以空闲页的起始页pgid集合,forwardmap和backmap用于释放的时候快速合并页),
将申请和释放时间复杂度降低到O(1),同时支持事务提交时,不持久化freelist,而是通过重启时扫描page重建,以提升etcd写性能、降低db大小。
集群稳定性
那些expensive read请求会导致etcd不稳定性:
- 简单的count only 查询。
- limit查询:将limit参数下推到索引层,实现查询性能百倍提升。
- 打包查询:
- etcd需要遍历treeIndex获取key列表。
- 获取到key列表、版本号后,etcd需要遍历boltdb,将key-value保存到查询结果到数据结构中。
快照
快照会影响db备份文件生成速度、Leader发送快照给Follower节点的资源开销、Follower节点通过快照重建恢复的速度。
网友评论