↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

etcd 3.7

etcd 与其它键值存储系统比较

etcd 3.7 开发、运维、升级、API 与内部原理中文指南

etcd 这个名称源自两个理念:Unix 系统中的“/etc”目录和“d”istributed(分布式)系统。其中,“/etc”目录是用于存储单个系统配置数据的路径,而 etcd 则用于存储大规模分布式系统的配置信息。因此,“d”istributed “/etc”即为 etcd。

etcd 旨在作为大规模分布式系统的通用基础架构。这类系统无法容忍脑裂(split-brain)运行,且愿意牺牲可用性以达成此目标。etcd 以一致且容错的方式存储元数据。etcd 集群旨在提供具备顶级稳定性、可靠性、可扩展性和性能的键值存储。

分布式系统使用 etcd 作为一致性的键值存储,用于配置管理、服务发现以及协调分布式任务。许多 组织 使用 etcd 实现生产系统,例如容器调度器、服务发现服务和分布式数据存储。使用 etcd 的常见分布式模式包括 领导者选举 、分布式锁 ,以及监控机器存活状态。

使用场景

  • Container Linux by CoreOS:在 Container Linux 上运行的应用程序可获得自动、零停机的 Linux 内核更新。Container Linux 使用 locksmith 协调更新过程。Locksmith 通过 etcd 实现分布式信号量,确保集群中任意时刻仅有一部分节点处于重启状态。
  • Kubernetes 将配置数据存储于 etcd,以实现服务发现与集群管理;etcd 的一致性对服务的正确调度与运行至关重要。Kubernetes API 服务器将集群状态持久化至 etcd。它使用 etcd 的监听(watch)API 监控集群,并推送关键配置变更。

对比图表

也许 etcd 已经看起来非常合适,但如同所有技术决策一样,仍需谨慎行事。请注意,本文由 etcd 团队编写。尽管理想情况是客观比较技术与功能,但作者的专业知识和倾向显然偏向 etcd。请仅按说明使用。

下表为快速对比 etcd 与其最流行替代方案差异的便捷参考。各列的进一步说明与详细信息请参见表格后的章节。

etcd ZooKeeper Consul NewSQL (Cloud Spanner, CockroachDB, TiDB)
并发原语 锁 RPC , 选举 RPC , 命令行锁 , 命令行选举 , Go 中的通用模式 外部 Curator 模式 (Java) 原生锁 API 罕见 ,若有也极少
线性一致读取 是 否 是 有时
多版本并发控制 是 否 否 有时
事务 字段比较、读取、写入 版本检查、写入 字段比较、锁、读取、写入 SQL 风格
变更通知 历史与当前键区间 当前键与目录 当前键与前缀 触发器(有时)
用户权限 基于角色 ACL ACL 不同(按表 GRANT ,按数据库 角色 )
HTTP/JSON API 是 否 是 很少
成员变更重配置 是 3.5.0 及以上 是 是
最大可靠数据库容量 数 GB 数百 MB(有时可达数 GB) 数百 MB 数 TB 以上
最小读取线性化延迟 网络 RTT 无读取线性化 RTT + fsync 时钟屏障(原子时钟、NTP)

Apache ZooKeeper

ZooKeeper 解决的问题与 etcd 相同:分布式系统协调与元数据存储。然而,etcd 拥有从 ZooKeeper 设计与实现的工程和运维经验中汲取的后见之明。从 ZooKeeper 中汲取的教训确实影响了 etcd 的设计,使其能够支持 Kubernetes 等大规模系统。etcd 相较于 ZooKeeper 的改进包括:

  • 动态集群成员关系配置
  • 高负载下的稳定读写
  • 多版本并发控制数据模型
  • 可靠的键监控,永不静默丢失事件
  • 租约原语将连接与会话解耦
  • 用于安全分布式共享锁的 API

此外,etcd 原生支持多种语言和框架。与 Zookeeper 采用其专属的自定义 Jute RPC 协议不同,该协议完全专属于 Zookeeper 并限制了 支持的语言绑定 ,etcd 的客户端协议基于 gRPC ,一个广受欢迎的 RPC 框架,提供 Go、C++、Java 等多种语言绑定。同样,gRPC 可通过 HTTP 序列化为 JSON,因此即使是一般的命令行工具如 curl 也能与其通信。由于系统可自由选择多种技术方案,它们通常基于 etcd 的原生工具链构建,而非围绕 etcd 采用单一固定的技栈。

在考虑功能、支持和稳定性时,计划使用 Zookeeper 作为一致键值存储的新应用程序应选择 etcd。

Consul

Consul 是一个端到端的服务发现框架。它提供内置的健康检查、故障检测和 DNS 服务。此外,Consul 还通过 RESTful HTTP API 暴露了一个键值存储系统。截至 Consul 1.0 版本 ,其存储系统在键值操作上的扩展性不如 etcd 或 Zookeeper 等系统;对于需要管理数百万个键的系统,将面临高延迟和内存压力。键值 API 缺少关键功能,尤其是多版本键、条件事务以及可靠的流式监听。

etcd 与 Consul 解决不同的问题。若需要分布式一致的键值存储,etcd 比 Consul 更为合适。若需要端到端的集群服务发现,etcd 功能不足;应选择 Kubernetes、Consul 或 SmartStack。

NewSQL (Cloud Spanner, CockroachDB, TiDB)

etcd 与 NewSQL 数据库(例如 Cockroach 、TiDB 、Google Spanner )均提供强数据一致性保证,并具备高可用性。然而,显著不同的系统设计参数导致其客户端 API 和性能特征存在显著差异。

NewSQL 数据库旨在跨数据中心水平扩展。这类系统通常将数据分区到多个一致的复制组(分片)中,这些分片可能分布于不同地理位置,存储的数据集规模可达数 TB 及以上。此类扩展方式导致其在分布式协调方面表现不佳,因为其依赖时钟等待,且更新操作的依赖图通常具有高度局部性。数据以表格形式组织,支持类似 SQL 的查询功能,语义比 etcd 更丰富,但相应地增加了查询处理、规划和优化的复杂性。

简而言之,选择 etcd 用于存储元数据或协调分布式应用。若需存储超过几 GB 的数据,或需要完整的 SQL 查询功能,则应选择 NewSQL 数据库。

使用 etcd 存储元数据

etcd 在单一一致的复制组内复制所有数据。对于存储几 GB 以内、需保持一致顺序的数据,这是最高效的方法。集群状态的每次修改(可能涉及多个键)都会被分配一个全局唯一标识,即 etcd 中的修订版本,该标识来自单调递增的计数器,用于推理操作顺序。由于仅存在一个复制组,修改请求只需通过 Raft 协议即可提交。通过将共识限制在单一复制组内,etcd 在采用简单协议的同时实现了分布式一致性,并达到了低延迟和高吞吐量。

etcd 的复制机制无法横向扩展,原因在于其缺乏数据分片。相比之下,NewSQL 数据库通常将数据分片存储于多个一致的复制组中,可存储规模达数 TB 及以上的数据集。然而,为确保每次修改都具有全局唯一且递增的 ID,每个请求必须在复制组之间通过额外的协调协议进行处理。这一额外的协调步骤可能导致全局 ID 冲突,迫使有序请求进行重试。最终结果是,该方案实现方式更为复杂,且在严格有序场景下的性能通常劣于 etcd。

如果应用程序主要关注元数据或元数据的顺序,例如用于协调进程,应选择 etcd。如果应用程序需要一个跨多个数据中心的大型数据存储系统,且对强全局顺序性要求不高,应选择 NewSQL 数据库。

使用 etcd 进行分布式协调

etcd 内置了分布式协调原语,包括事件监听、租约、选举以及分布式共享锁(请注意,使用分布式共享锁时,用户需了解其非显而易见的特性,详情见下文)。这些原语均由 etcd 开发者维护和支持;若将这些原语交由外部库实现,则等于推卸了开发基础分布式软件的责任,本质上使系统变得不完整。NewSQL 数据库通常期望这些分布式协调原语由第三方提供。类似地,ZooKeeper 以其独立的 协调算法库 著称。Consul 提供原生锁 API,甚至坦言其“并非牢靠的方法 ”。

理论上,可以在任何提供强一致性的存储系统之上构建这些原语。然而,相关算法往往十分微妙;很容易设计出看似可行的锁机制,却因惊群效应和时序偏差而突然失效。此外,etcd 支持的其他原语(如事务内存)依赖于 etcd 的 MVCC 数据模型;仅具备简单的强一致性是不够的。

在分布式协调场景中,选择 etcd 可以避免运维困扰并节省工程投入。

关于锁和租约的使用说明

etcd 提供 lock APIs ,其基于 租约机制 以及 etcd 中的实现 。租约机制的基本思想是:服务器向请求客户端授予一个令牌,称为租约。当服务器授予租约时,会为其关联一个 TTL。当服务器检测到时间流逝超过 TTL 时,将撤销该租约。只要客户端持有未被撤销的租约,即可声称其拥有与该租约关联的资源的访问权。在 etcd 中,该资源是 etcd 键空间中的一个键。etcd 通过此机制提供锁 API。然而,锁 API 本身不能作为互斥机制使用。API 被称为锁,是由于 历史原因 。不过,如以下所述,锁 API 可作为互斥机制的优化手段使用。

租约机制最重要的方面是,TTL 被定义为物理时间间隔。服务器和客户端均使用各自的时钟来衡量时间的流逝。这可能导致一种情况:服务器已撤销租约,但客户端仍声称自己拥有该租约。

租约机制如何保证锁机制的互斥性?实际上,租约机制本身并不能保证互斥性。拥有租约并不能保证租约持有者持有该资源的锁。

在使用 etcd 锁控制对 etcd 自身键的互斥访问时,互斥性基于版本号验证机制实现(在其他系统如 Consul 中有时称为比较并交换)。在 etcd 的 RPC 接口中,例如 Put 或 Txn,可指定操作所需的修订版本号和租约 ID 条件。若条件不满足,操作可能失败。借助此机制,etcd 为客户端提供分布式锁功能。这意味着,当客户端请求被 etcd 集群成功处理时,客户端可确认其已获取到某键的锁。

在分布式锁的文献中,类似的架构设计被描述如下:

  • 在 Chubby 论文中,引入了 sequencer 的概念。我们理解该 sequencer 与 etcd 中的修订版本号和租约 ID 的组合几乎相同。
  • 在 如何实现分布式锁 中,Martin Kleppmann 提出了 fencing token 的概念。作者认为,在 etcd 场景下,fencing token 即为修订版本号。
  • 在 分布式系统中同步时钟的实际应用 中,可以找到描述:Thor 基于版本号验证和租约实现了一种分布式锁机制。

为何 etcd 及其他系统在基于版本号验证实现互斥的同时仍提供租约机制?这是因为租约可作为优化机制,有效减少被中止请求的数量。

请注意,etcd 键的锁定可高效实现,这是由于租约机制和版本号验证机制的共同作用。若用户需要保护与 etcd 无关的资源,则这些资源必须提供类似 etcd 键的版本号验证机制以及副本一致性保障。etcd 自身的锁功能无法用于保护外部资源。

来源与许可

文档取自 pig.center · 上游文档

版本
3.7
许可
CC-BY-4.0
来源修订
dba8dc7e1afd3f6eea300ebf9ecb67c731145f1ee7db1ca0d3ebc49508812609
译文修订
dba8dc7e1afd3f6eea300ebf9ecb67c731145f1ee7db1ca0d3ebc49508812609
原始 Markdown