本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
前提回顾
在上一篇文章,我们已经探讨了在处理数据一致性问题中的角色。当面对数据时序的挑战时,这意味着我们已主动放弃了对强一致性(Strong Consistency)的坚守,转而追求最终一致性(Eventually Consistency)这一更为宽松的标准。
最终一致性
最终一致性并非承诺任何时候、任何节点的数据状态皆完全一致,而是保证在数据更新停止后,所有节点最终将达成一种协调一致的状态。这一概念乍一听似乎充满了不确定性,让人不禁产生疑虑。
Clock时钟机制
解决执行顺序的难题,诸多系统采用了Clock时钟 技术,通过消息传递机制来梳理和推断事件发生的先后次序,时钟机制依赖于节点间逻辑时间戳的交换来构建时序关系。
局限性
如果节点与其他节点通信不畅,或者时钟不同步,孤立运行或消息交换频率极低,那么由此得出的时序信息便会失准。
Clock并不承诺能够完美重建全局的全序关系(total order),而仅能确保部分有序关系(partial order)。它可能仅能告诉我们诸如"A事件早于D事件发生"、"B事件早于D事件发生"、"C事件早于D事件发生"这类相对顺序信息,而对于A、B、C三者之间的直接时序关系,则因缺乏全序信息而无法确定。 最终一致性及其依赖的"Clock时钟机制"在确保数据时序准确性方面的确存在局限性。那么,为何我们仍会选择使用这种看似不可靠的机制呢?在于权衡与取舍。在特定场景下,我们愿意牺牲强一致性,以换取其他关键特性,如更高的系统可用性、更低的延迟或更易于扩展的架构。
CAP协议
接下来,我们将进一步探讨CAP定理,这一理论揭示了在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)三个重要属性无法同时完美满足,系统设计者必须在三者间做出抉择,最多只能实现其中任意两个属性的理想状态。
CAP理论的三要素
在构建分布式系统时,我们通常要求系统必须具备分区容忍性P。这意味着即使在网络分区或节点故障的情况下,系统仍能保持一定的可用性和功能。这也意味着我们需要在强一致性C和可用性A之间做出权衡和取舍,以找到最适合系统需求的平衡点。
- C(强一致性): 在任何时刻,无论我们从集群中的哪两个节点获取状态信息,得到的结果都应是完全一致的。
- 确保了数据的高度一致性和准确性,使得集群中的各个节点始终保持在同步状态。
- A(可用性): 只要集群中的某个节点没有出现故障或损坏,它就必须能够正常提供服务。
- 确保了系统的高可用性,即使面临部分节点故障或负载不均的情况,也能保证用户请求得到及时处理。
- P(分区容忍性): 当集群由于网络问题或节点故障等原因被分割成两个或更多不完整的子集群时,整个系统仍能够保持正常运作。
- 分区容忍性使得系统能够在复杂和不确定的网络环境下保持稳定性和可靠性,从而为用户提供持续的服务。
A和C机制的保障
在分布式系统中,通常存在一种权衡:这三个关键特性(强一致性C、可用性A和分区容忍性P)最多只能同时满足两个。
假设,我们有两台位于不同机器上的节点,并采用2PC(两阶段提交)机制来存储数据,即只有当所有节点都同意后,数据才会被存入。
- A:多个机器上存储数据,能保证可用性
- C:2PC机制,保证每个节点的数据都一致
但是,一旦系统发生分区或者任何一个节点出现故障,整个系统就会陷入无法运作的状态。在这种情况下,虽然强一致性C和可用性A得到了保障,但分区容忍性P却无法实现。
P分区容错性
假设我们有两台节点部署在不同的机器上,并且这些节点必须能够容忍网络分区的情况。那么,当分区真的发生时,我们应该如何应对呢?
AP机制的保障
选择保持系统的可用性。即使两个节点之间无法相互通信,只要它们都还在运行,就可以让它们继续提供正常服务。
这样的做法会导致一个问题:其中一个节点上的变化无法传递到另一个节点,因此两边的数据可能会变得不一致。更糟糕的是,可能会出现裂脑的情况,即两边都认为自己是主节点并尝试写入数据,这就会引发我们之前讨论过的实践顺序问题。我们牺牲了数据的一致性来换取系统的可用性。(无一致性------A和P)
CP机制的保障
选择是保持数据的一致性。为了避免之前提到的不一致问题,我们必须在分区发生时停掉其中一个节点。
这样做可以确保数据在任何时候都是一致的,但代价是牺牲了系统的可用性。因为在停掉节点的期间,系统无法处理任何请求或提供服务。(无可用性------C和P)
分布式系统方向
在分布式系统中,当面对网络分区的情况时,我们需要在可用性和一致性之间做出权衡。根据系统的具体需求和业务场景,我们可以选择牺牲其中一个特性来换取另一个特性的保障。 在实际应用中,我们很难完全避免网络分区或节点故障的发生。如果我们将请求分散到所有机器上执行,一旦某个节点出现故障或网络分区,整个系统就可能受到影响。
分布式系统之Zookeeper
Zookeeper在分布式系统中扮演着举足轻重的角色。无论是用于维护元数据、实现分布式锁,还是作为命名服务和高可用性的保障,
ZK的作用和职责
在当前众多的分布式系统中,Zookeeper被广泛应用,其强大而灵活的功能为系统提供了坚实的支撑,Zookeeper可用于维护分区元数据。
协调服务
Zookeeper帮助系统实现各种分布式协调功能。例如,它可以用来管理分布式应用程序中的元数据,确保数据的一致性和准确性。此外,还可以用来实现分布式锁,帮助多个进程或线程在访问共享资源时避免冲突。
命名服务
Zookeeper还可以作为命名服务,为分布式系统中的各个节点提供统一的命名和寻址机制。通过Zookeeper,我们可以轻松地定位和管理分布式系统中的各个组件,实现服务的动态发现和注册。
构建高可靠服务
能够在部分节点故障或网络分区的情况下保持系统的正常运行。这使得Zookeeper在构建高可靠性的分布式系统时具有得天独厚的优势。
ZK的常见用法
-
共享元数据:实现元数据的共享,确保在分布式系统中,各个节点能够访问和更新统一、一致的元数据信息。
-
监控成员节点状态:实时监控集群中各个成员节点的健康状况,包括节点的在线状态、负载情况等。
-
维护集群成员列表:动态维护集群的成员列表,一旦有节点出现故障或离线,能够迅速感知并做出相应的调整,确保集群的稳定性和可用性。
-
选举Leader节点:利用分布式协调机制,能够高效地协助完成Leader节点的选举工作。一旦选举完成,Leader节点将承担起管理集群、协调各个节点工作的重任,确保整个系统的正常运行。
ZK基本原理
ZK的数据以树状结构进行组织,其节点称为znode,可用于存放数据。 由于znode中的数据对于众多元素而言至关重要,ZK设计了一套通知机制。当znode中的数据发生更新时,ZK能够及时向那些事先对特定znode注册了watcher的进程发送通知。 临时节点:临时节点是一种特殊的znode称为ephemeral node,它主要用于监控集群成员的状态。这种znode与创建它的成员的session状态紧密关联。如果成员的session进行销毁,那么相应的ephemeral node将被自动删除。
因此,其他成员若在此ephemeral node 上设置了watcher,便能在检测到节点挂掉(即session失效)时收到通知。 为了确保系统的稳定性和高可用性,所有成员都会对与master关联的ephemeral node注册watcher。一旦master失效,能够侦测到这一变化的成员将触发leader election过程,从而确保集群能够迅速恢复并继续提供服务。
ZK的顺序一致性
ZK的设计确保了全局顺序的一致性,其核心在于仅由Leader处理写入请求。即便在发生网络分区的情况下,依然能够维持服务的可用性,这得益于之前提到的Quorum算法(达到最低投票门槛的成员集合),ZK是一个偏向于一致性(C)和可用性(A)的分布式系统。
ZK的成员角色分为Leader和Follower两种(学习者不参与投票,暂时忽略不计)。在一个Quorum中,最多只能有一个Leader,其余均为Followers。
ZK的选举原则
Leader选举的目的便是为了从Quorum中选出一位Leader。一个Quorum的成员数需达到最低投票门槛,这通常意味着成员数应大于ZK节点总数的一半。
例如,若共有5个ZooKeeper节点,则Quorum中至少应有三个成员(3>2.5),这样的设计确保了即使集群被分割成两部分,其中一个分区的节点数也不足以形成Quorum。当不足以形成Quorum时,该分区将不允许对外提供服务。
ZK的数据安全性
ZK将硬盘视为磁带般的存储介质,而将数据主要存放在DRAM(或暂时不考虑的Flash)中。然而,单机DRAM的容量终究有限,因此我们需要借助一系列技术手段,如数据压缩、分布式系统等,来应对这一挑战。
数据的持久化保存
DRAM具有易失性,一旦机器断电,存储其中的数据便会消失无踪。如何确保数据在断电后不会丢失呢?这里有两个可行的选项:
- 采用持久化策略,即在硬盘上记录变化日志。这样,即使DRAM中的数据丢失,我们也可以通过读取这些日志来恢复数据。
- 采用复制策略,在其他机器上创建数据的副本。这样,即使某一台机器的DRAM数据丢失,其他机器上的副本仍然可用。