读《云原生分布式存储基石:etcd 深入解析》
最近一个月时间一直都在忙着搬家,国庆又带老婆回家待了几天,一直都没什么时间静下心来看看书,终于国庆过完了,又开一个新坑。这本书是华为做容器服务的团队写的,etcd 的源码大家多多少少都看过一些,这本书系统过了一下整个 etcd 的设计和一些重要的设计亮点和实现原理,对于我们自己对 etcd 的理解也能有一个梳理和印证。
本书分为三个大部分,分布式基础、 etcd 实战以及源码分析,总共十二章。
分布式基础
分布式系统的设计目标主要有以下几个方面:
- 可用性:可用性是分布式系统的核心需求,其用于衡量一个分布式系统持续对外提供服务的能力。可用性方面,主要是通过系统冗余(备份、多节点)来保证容灾。
- 可扩展性:增加机器后不会改变或极少改变系统行为,并且能获得近似线性的性能提升。这一点对于业务增长来说十分重要。
- 容错性:系统发生错误时,具有对错误进行规避以及从错误中恢复的能力。数据中心能够出现的错误往往超过你的想象,怎么在出现错误时提供稳定的服务,这一点在设计时也需要深入思考。
- 性能:对外服务的响应延时和吞吐率要能满足用户的需求。这点就不多说了,尤其是 ToC的业务,系统响应往往决定了用户的留存。
这里又要老生常谈 CAP 了,我们复习一下,CAP 分别是一致性(Consistency)、可用性(Availability)和分区容忍性(Tolerance to the partition of network)。分布式环境下网络分区是常态,所以分布式系统往往是AP 或者 CP,要么提高可用性降低一致性需求,要么要求强一致性但是可用性会降低。一般除了一些特定的场景(比如金融业务)会要求 CP,其他基本都是以 AP 为主,满足最终一致性即可。
我们重点聊聊一致性,首先什么是一致性,在分布式场景下,用通俗的话来说,就是怎么保证多个副本的数据是否一致,或者说系统对外呈现的状态是否一致。对于一致性,可以分别从客户端和服务端两个不同的视角来理解。从客户端来看,一致性主要是指多并发访问时如何获取更新过的数据的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终的一致性。因此,可以从两个角度来查看一致性模型:以数据为中心的一致性模型和以用户为中心的一致性模型。
以数据为中心的一致性模型有强一致性(Strong Consistency)、顺序一致性(Sequential Consistency)、因果一致性(Causal Consistency)、可串行化一致性(Serializable Consistency)
强一致性(Strong Consistency),顺序一致性(Sequential Consistency),因果一致性(Causal Consistency),和可串行化一致性(Serializable Consistency)是分布式系统中描述不同一致性级别的概念。它们之间的区别主要在于对于并发操作的处理和数据操作顺序的要求。
强一致性(Strong Consistency): 在一个强一致性系统中,任何时刻,客户端读取的数据都是最新的,而且所有节点上的数据副本都是完全一致的。强一致性要求系统保证所有节点上的读操作都能够读取到最近一次的写操作结果。这种一致性保证通常需要在分布式系统中采用复杂的同步和协调机制,因此可能会影响系统的性能和可用性。
顺序一致性(Sequential Consistency): 顺序一致性保证所有节点上的操作都会被按照相对于其他操作的发生顺序来进行。也就是说,如果一个操作在系统中发生在另一个操作之前,那么所有节点上的观察结果都应该反映这个发生顺序。不过,顺序一致性并不要求所有节点上的操作立即被所有其他节点看到,可能会有一定的延迟。
因果一致性(Causal Consistency): 因果一致性是一种比顺序一致性更弱的一致性模型。在因果一致性下,系统保证具有因果关系的操作会被按照其发生的顺序来进行,但不保证并发操作之间的顺序。如果一个事件A影响了事件B,那么观察到事件A的节点在事件B之前就应该知道事件A发生了。
可串行化一致性(Serializable Consistency): 可串行化一致性是一种强一致性模型,它要求系统的执行序列等价于某个串行执行序列(即在一个时刻只有一个操作被执行)。这意味着系统的并发操作不能产生与某个串行执行序列不一致的结果。可串行化一致性是一种非常严格的一致性模型,通常需要使用各种事务处理技术来实现。
以用户为中心的一致性模型主要是指实际业务需求中需要满足针对某个用户业务的一致性,比如最终一致性,即允许一个不一致性窗口,只要最终所有副本达到一致性即可。
当同一份数据存在多个副本时,我们通过复制状态机来管理它们。复制状态机(Replicated State Machine)是一种分布式系统的设计模式,用于确保在多个节点上保持相同的状态副本。在复制状态机模型中,多个服务器节点通过相互通信和协作来保持一致的状态,并且客户端可以向任意一个节点发送请求,系统会确保处理请求的顺序和结果在所有节点上都是一致的。
复制状态机的核心思想是将分布式系统中的状态看作是一个状态机,这个状态机会接收客户端的请求并且按照特定的顺序进行处理,从而达到一致的状态。为了实现复制状态机,通常会使用一种称为共识算法(Consensus Algorithm)的技术,例如Paxos、Raft等。这些共识算法可以确保系统中的节点在面对故障和网络分区等问题时,依然能够保持一致的状态。
FLP(Fisher, Lynch, Paterson)定理是分布式计算领域的一个基本定理,指出在异步网络中,当存在一个或多个节点发生故障时,不可能设计一个算法,能够在有限的时间内,保证所有节点对一个共享的变量(例如确定某个决策)达成一致的协议。简而言之,FLP定理指出,在异步网络中,不可能同时满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)这三个分布式系统设计的基本要求。
具体来说,FLP定理的内容是:在一个异步网络中,如果有一个或多个节点可能发生故障,且网络不提供时钟同步(即消息传递没有上限,消息可能在任何时间间隔内被传递),那么在有限的时间内不可能设计一个分布式算法,能够确保所有节点对某个共享的变量达成一致的协议。
这个定理的结论有重要的实际意义,它告诉我们,在异步网络环境下,无法设计一个能够在所有情况下都保证一致性的分布式算法。因此,在实际系统设计中,需要根据具体需求和系统特性,权衡一致性、可用性和分区容忍性,选择适当的系统模型和算法。根据FLP定理,实际的一致性协议(Paxos、Raft等)在理论上都是有缺陷的,最大的问题是理论上存在不可终止性!至于Paxos和Raft协议在工程的实现上都做了哪些调整(例如,Paxos和Raft都通过随机的方式显著降低了发生算法无法终止的概率),从而规避了理论上存在的哪些问题,下文将会有详细的解释。
书中接下来讲到了 Paxos 、 Raft算法,这两个协议在网上都有大量的介绍,我们不多赘述,只简单说一下 Raft,Raft把一致性问题分解成了领袖选举(leader election)、日志复制(log replication)、安全性(safety)和成员关系变化(membership changes)这几个子问题,我们在面试时候如果聊到 Raft 可以从这几个角度逐一介绍。
实战篇
为什么使用 etcd?在聊到 etcd 的时候,我们往往会聊到类似的其他系统,比如 zookeeper,与 zookeeper 相比,etcd 更加稳定可靠。在服务发现的实现上,etcd使用的是节点租约(Lease),并且 支持Group(多key);而ZooKeeper使用的是临时节点。etcd支持稳定的watch,而不是ZooKeeper一样简单的单次触发式 (one time trigger)watch。etcd支持MVCC(多版本并发控制),因为有协同系统需要无锁操作。 etcd 支持更大的数据规模,支持百万到千万级别的 key。 etcd 性能更好,在 3 台八核云服务器上部署的 etcd v3 可以实现每秒数万次的写操作和数十万次的读操作。
etcd 的官方定义是“A distributed, reliable key-value store for the most critical data of a distributed system.”本质上是为了提供可靠的分布式键值存储,用来存储分布式系统中的最关键的数据。 etcd 的常见使用场景有:服务发现、分布式锁、分布式数据队列、分布式通知和协调、主备选举等。(p.s.我自己就是做服务发现相关的业务,不过是在一个没有强一致性的环境,在这种场景下怎么保证服务发现的一致性其实也是一个很有意思的问题,我们在生产环境做了大量的 workaround,这个以后有机会可以聊一下)
etcd 基于 Raft 协议,通过日志复制来保证数据的强一致性,简单来说,客户端在写一个 key 的时候,首先会存储到 etcd 的 leader 节点上,接着通过 Raft 协议同步到集群中所有成员中,以此维护各成员的一致性。准确地说,只有在多数节点 ack 了这个写操作,这个写操作才算成功。etcd 能够容忍集群中(n-1)/2 个节点的故障。 etcd 的数据通过 WAL 来持久化,数据操作先写 WAL,再做提交。 etcd 通过快照来记录某一时刻的所有数据,默认每一万条数据做一次快照,快照后 WAL 即可删除。
etcd 单实例支持每秒一千次以上的写操作,极限情况下 QPS 可以达到 10k 。