程序锅

  • 首页
  • 分类
  • 标签
  • 归档
  • 关于

  • 搜索
基础知识 Etcd LeetCode 计算机体系结构 Kubernetes Containerd Docker 容器 云原生 Serverless 项目开发维护 ELF 深入理解程序 Tmux Vim Linux Kernel Linux numpy matplotlib 机器学习 MQTT 网络基础 Thrift RPC OS 操作系统 Clang 研途 数据结构和算法 Java 编程语言 Golang Python 个人网站搭建 Nginx 计算机通用技术 Git

Etcd DB Quota

发表于 2023-01-15 | 分类于 Etcd | 0 | 阅读次数 3575

DB Quota 概述

Etcd DB 的 quota 是由参数 --quota-backend-bytes 决定:

  • 填 0 的话,使用的是 etcd 默认的 2GB。
  • 当填小于 0 的话,会禁用 quota 功能。不建议这么做,因为这会让 db 处于失控状态,导致性能下降。
  • 填大于 0 的值时,社区建议不超过 8GB(可以参考下文)。

以下几种情况会导致 etcd db 超过 quota:

  • 集群规模与 quota 的配置不匹配,前者过大后者过小。db 配置的值较小,或者采用默认配置时,当集群规模增大、写入的 QPS 增多之后,etcd db 的大小就可能会很快超过 2G。

  • 没有配置 compact 策略或者 compact 配置策略不当。etcd v3 是一个 MVCC 数据库,保存了 key 的历史版本。如果未配置 compact 策略的话,db 大小会不断增大,超过 quota。

当超过 quota 后,相应的解决方法是:

  1. 调大 quota,也就是调整 --quota-backend-bytes 的参数。

  2. 调大 quota 后,需要发送一个取消 alarm 的命令(etcdctl alarm disarm)以消除告警。这是因为,超过 quota 之后,etcd 会产生一个 NO SPACE 的 alarm(告警)请求,并通过 Raft 日志同步给其他节点,同时会将告警持久化到 db 中。而 Apply 模块在执行 commited proposal 的时候,会先检查当前是否存在这个 alarm,如果有的话则拒绝写入,因此需要将这个 alarm 去掉。

  3. 检查 etcd 的 compact 功能是否开启、配置策略是否合理。

为什么社区建议 db 文件不超过 8GB

Etcd 社区对 db 大小是不建议超过 8GB,主要的考虑是大数据量对 Etcd 集群会有以下影响,

  1. 启动耗时。

    etcd 启动的时候,会打开 db 文件,读取 db 文件中所有的 key-value 数据,用于重建内存 treeIndex 模块。因此大量的 key 会导致 etcd 启动过慢。

    etcd 重建 treeIndex 模块的过程如下,

    • 首先是启动 goroutine。它会负责遍历 boltdb,获取所有 key-value 数据,并将其反序列化为 etcd 的 mvccpb.KeyValue 结构。由于 etcd 中存储的 key 是 revesion,是有序的,因此它会从 1 开始批量遍历,每次查询 10000 条 key-value 记录,直到查询数据为空。
    • 其次是构建 treeIndex 索引的 goroutine。它从主 goroutine 获取 mvccpb.KeyValue 数据,基于 key、版本号、是否带删除标识等信息,构建 keyIndex 对象,并插入到 treeIndex 模块的 B-tree 中。因可能存在多个 goroutine 并发操作 treeIndex,比如 compaction 异步任务也会操作 treeIndex,因此 treeIndex 的 Insert 函数会加锁。但是,需要注意的是 etcd 启动时,只有一个 goroutine 负责构建 treeIndex 索引。
  2. 节点内存配置。

    etcd 启动时,会通过 boltdb 的 Open API 获取数据库对象,而 Open API 会通过 mmap 机制将 db 文件映射到内存中。由于设置了 mmap 的 MAP_POPULATE flag,此时会预先将待映射文件的内容,在这里就是 db 文件,加载到内存中,而不是在实际访问页面时才进行按需加载。因此在节点内存充足的情况下,db 文件的内容会全被加载到内存中,此时看到的 etcd 占用内存,一般是 db 文件大小与内存 treeIndex 之和。client 后续发起的 etcd 读操作,都是直接通过内存获取到 boltdb 的 key-value 数据,不会产生任何磁盘 IO,具备良好的读性能、稳定性。

    但是,当节点内存空间不足的时候,比如当 db 文件超过节点内存配置时,若查询的 key 所相关的 branch page、 leaf page 都不在内存时,就会频繁触发主缺页中断,导致读延时抖动、QPS 下降。

    因此为了保证 etcd 集群性能的稳定性,建议 etcd 节点内存规格要至少大于 etcd db 文件大小。

  3. 快照。

    当 Follower 节点落后 Leader 较多数据的时候(比如新加入一个 etcd 节点的时候),会通过快照传输(snapshotting)的方式快速同步状态。

    • 此时 Leader 会从 boltdb 中读取内容来生成快照内容,通常涉及将整个键值存储序列化到一个快照文件中。此时的快照内容包含了能够重建 boltdb 的所有信息,包括键值数据和必要的元数据,如快照时的索引和任期(term)。因此较大的 db 文件会导致 Leader 生成快照消耗较多的 CPU。
    • Leader 生成快照后,会将快照分割成小文件,发送给 Follwer。此时会消耗较多的网络带宽资源。
    • Follower 在收到快照之后,会进行重建,此过程耗时会较长。并且由于耗时较长,在集群写 QPS 较大的时候,基于快照重建后的 Follower 依然无法通过正常的日志复制模式来追赶 Leader,只能继续触发 Leader 生成快照,进而进入死循环, Follower 一直处于异常中。
  4. treeIndex 索引性能。

    etcd 不支持数据分片(shard),所有的信息都保存在内存中的 treeIndex 上。如果 db 文件过大,也就意味着有几十万到上千万的 key。此时查询、修改操作的延时都会增加。

  5. boltdb 性能。

    boltdb 在提交事务(commit)时偶尔会出现较高延时。这个在上述讲 freelist 的时候提到过。

    • 一方面是因为提交(commit)的时候,B+tree 会进行重平衡和分裂,此时可能会从 freelist 中申请若干连续的 page 存储数据,或释放空闲的 page。当申请一个连续的 n 个 page 存储数据时,它会遍历 boltdb 中所有的空闲页,直到找到连续的 n 个 page。因此它的时间复杂度是 O(N)。若 db 文件较大,又存在大量的碎片空闲页,则会导致延时增加,甚至很可能导致超时。同时也可能会释放若干个 page 给 freelist,在合并到 freelist 时,时间复杂度是 O(NLog N)。

      针对这个问题,etcd 社区在 bbolt 项目中,实现了基于 hashmap 来管理 freelist。通过引入了如下的三个 map 数据结构(freemaps 的 key 是连续的页数, value 是空闲页的起始页 pgid 集合,forwardmap 和 backmap 用于释放的时候快速合并页),将申请和释放时间复杂度降低到了 O(1)。

      freemaps map[uint64]pidSet
      forwardMap map[pgid]uint64
      backwardMap map[pgid]uint64
      

      现在可以通过 bbolt 的 FreeListType 参数来控制使用 array 还是 hashmap。在 etcd 3.4 版本中还是 array,3.5 版本将默认是 hashmap。

    • 另一方面,db 中若存在大量空闲页,持久化 freelist 也需要消耗较多的 db 大小,并会导致额外的事务提交延时。

      bbolt 支持启动时扫描全部 page 来构造 freelist,降低了 db 大小和提升写事务提交的性能(但是它会带来 etcd 启动延时的上升)。此行为可以通过 bbolt 的 NoFreelistSync 参数来控制,默认是 true 启用此特性。

  6. 集群稳定性。

    db 文件增大后,另外一个非常大的隐患是用户 client 发起的 expensive request,容易导致集群出现各种稳定性问题。本质原因是 etcd 不支持数据分片,各个节点保存了所有 key-value 数据,同时它们又存储在 boltdb 的一个 bucket 里面。当集群含有百万级以上 key 的时候,任意一种 expensive read 请求都可能导致 etcd 出现 OOM、丢包等情况发生,而这主要原因是因为 etcd 会将查询的结果先缓存在内存中。比如,

    • count only 查询。在 etcd 3.5 版本之前,统计 key 数会遍历 treeIndex,把 key 追加到数组中。然而当数据规模较大时,追加 key 到数组中的操作会消耗大量内存,同时数组扩容时涉及到大量数据拷贝,会导致延时上升。
    • limit 查询。在 etcd 3.5 版本之前,当执行范围查询(例如,用于获取键的范围)时,如果用户指定了一个 limit 参数来限制返回结果的数量,etcd 的行为是首先检索整个范围的键,然后在 API 层应用 limit,这意味着即使用户只需要少量结果,etcd 也需要从 treeIndex 中检索更多的项,导致内存增加和时间消耗。etcd 3.5 中将 limit 参数下推到了 treeIndex,实现了查询性能百倍提升。简单来说,就是在处理查询时,它会考虑 limit 参数,当遍历达到限制数量的键值对时就返回。
    • 大包查询。比如未分页批量遍历 key-value 数据(比如一次查询万级别的 key 时)或单 key-value 数据较大的时候。由于会将 key-value 保存到查询结果数据结构中,会导致内存占用变高。随着请求 QPS 增大,极易出现 OOM、丢包等。 etcd 这块未来的优化点是实现流式传输。
卷死我
dawnguo 微信支付

微信支付

dawnguo 支付宝

支付宝

  • 本文作者: dawnguo
  • 本文链接: /archives/270
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Kubernetes # Etcd
boltdb 介绍
Etcd Compact
  • 文章目录
  • 站点概览
dawnguo

dawnguo

215 日志
24 分类
37 标签
RSS
Creative Commons
© 2018 — 2025 程序锅
0%