这些分布式理论对你很重要(CAP | BASE | ACID)

1. CAP定理的含义

对于设计开发分布式系统的架构师来说,这些分布式理论对你很重要。分布式系统的难点就是各个节点间的状态如何同步,CAP是这方面的定理,也是分布式系统的起点。

分布式系统的三个指标

在这里插入图片描述
分布式系统有三个指标,分别是

  • Consistency
  • Availability
  • Partition tolerance

它们的第一个字母分别是 C、A、P,这三个特性不能同时做到,这就叫CAP理论。

分区容错

分区容错的意思是,区间通信可能失败。即使因为网络原因系统之间的消息丢失或者延迟了,系统依然是可以操作的。
在这里插入图片描述
像上图中,G1和G2是两台跨区域的服务器,G1给G2发消息可能会没有收到,在分布式系统设计的时候必须考虑这种情况。一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。

一致性

一致性是说每次读出的数据都应该是最近一次写入的数据或返回错误,而不应该返回过期的数据,数据是一致的。
在这里插入图片描述
这是一个非一致性的系统,当客户端向G1写入v1,但是当用户从G2节点读取数据时,这时候数据返回的仍是v0,不满足一致性。
在这里插入图片描述
在满足一致性的系统中,当G1写入v1数据,之后同步给G2,当客户端无论从哪个节点读取时,读到的数据都是v1。这样就满足了一致性要求。

可用性

可用性是说,每次请求都应该得到一个响应而不是返回一个错误或是失去响应,不过这个响应不保证是最近写入的,也就是说系统是一直可以使用的,不会引起调用者的异常,但是并不保证响应的数据是最新的。

三者的矛盾

当系统要必须满足一致性时,也就是上面的例子中客户端从G1或从G2取到的数据都是一样的,这样就会依赖于G1向G2完成数据的同步,在数据同步期间整个操作处于锁定状态,G2不能用于读写,这样就丢失了可用性。同时,期间可能出现通讯失败,即分区容错。

2.分布式事务BASE理论

BASE理论是对CAP的延伸和补充,是对CAP中的AP方案的一个补充,即使在选择AP方案的情况下,如何更好的最终达到C。

BASE是基本可用,柔性状态,最终一致性三个短语的缩写,核心的思想是即使无法做到强一致性,但应用可以采用适合的方式达到最终一致性。

3.ACID

ACID是传统数据库常用的设计理念,追求强一致性模型,关系数据库的ACID模型拥有高一致性+可用性,所以很难进行分区,所以在微服务中ACID已经是无法支持,我们还是回到CAP去寻求解决方案。

实现最终一致性

弱一致性:系统不能保证后续访问返回更新的值。需要在一些条件满足之后,更新的值才能返回。从更新操作开始,到系统保证任何观察者总是看到更新的值的这期间被称为不一致窗口。
最终一致性:这是弱一致性的特殊形式;存储系统保证如果没有对某个对象的新更新操作,最终所有的访问将返回这个对象的最后更新的值。

BASE模型

BASE模型是传统ACID模型的反面,不同与ACID,BASE强调牺牲高一致性,从而获得可用性,数据允许在一段时间内的不一致,只要保证最终一致就可以了。
BASE模型反ACID模型,完全不同ACID模型,牺牲高一致性,获得可用性或可靠性: Basically Available基本可用。支持分区失败(e.g. sharding碎片划分数据库) Soft state软状态 状态可以有一段时间不同步,异步。 Eventually consistent最终一致,最终数据是一致的就可以了,而不是时时一致。

4.CAP 在实际中应用的例子

服务注册中心

在讨论CAP之前先明确下服务注册中心主要是解决什么问题:一个是服务注册,一个是服务发现。

  • 服务注册:实例将自身服务信息注册到注册中心,这部分信息包括服务的主机IP和服务的Port,以及暴露服务自身状态和访问协议信息等。

  • 服务发现:实例请求注册中心所依赖的服务信息,服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务。

目前作为注册中心的一些组件大致有:dubbo的zookeeper,springcloud的eureka,consul,rocketMq的nameServer,hdfs的nameNode。目前微服务主流是dubbo和springcloud,使用最多是zookeeper和eureka

  • zookeeper选择CP
    zookeep保证CP,即任何时刻对zookeeper的访问请求能得到一致性的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务的可用性。从实际情况来分析,在使用zookeeper获取服务列表时,如果zk正在选举或者zk集群中半数以上的机器不可用,那么将无法获取数据。所以说,zk不能保证服务可用性。

  • eureka选择AP
    eureka保证AP,eureka在设计时优先保证可用性,每一个节点都是平等的,一部分节点挂掉不会影响到正常节点的工作,不会出现类似zk的选举leader的过程,客户端发现向某个节点注册或连接失败,会自动切换到其他的节点,只要有一台eureka存在,就可以保证整个服务处在可用状态,只不过有可能这个服务上的信息并不是最新的信息。

  • zookeeper和eureka的数据一致性问题
    先要明确一点,eureka的创建初心就是为一个注册中心,但是zk更多是作为分布式协调服务的存在,只不过因为它的特性被dubbo赋予了注册中心,它的职责更多是保证数据(配置数据,状态数据)在管辖下的所有服务之间保持一致,所有这个就不难理解为何zk被设计成CP而不是AP,zk最核心的算法ZAB,就是为了解决分布式系统下数据在多个服务之间一致同步的问题。
    更深层的原因,zookeeper是按照CP原则构建,也就是说它必须保持每一个节点的数据都保持一致,如果zookeeper下节点断开或者集群中出现网络分割(例如交换机的子网间不能互访),那么zk会将它们从自己的管理范围中剔除,外界不能访问这些节点,即使这些节点是健康的可以提供正常的服务,所以导致这些节点请求都会丢失。
    而eureka则完全没有这方面的顾虑,它的节点都是相对独立,不需要考虑数据一致性的问题,这个应该是eureka的诞生就是为了注册中心而设计,相对zk来说剔除了leader节点选取和事务日志极致,这样更有利于维护和保证eureka在运行的健壮性。
    再来看看,数据不一致性在注册服务中中会给eureka带来什么问题,无非就是某一个节点被注册的服务多,某个节点注册的服务少,在某一个瞬间可能导致某些ip节点被调用数少,某些ip节点调用数少的问题。也有可能存在一些本应该被删除而没被删除的脏数据。

  • 服务注册应该选择AP还是CP
    对于服务注册来说,针对同一个服务,即使注册中心的不同节点保存的服务注册信息不相同,也并不会造成灾难性的后果,对于服务消费者来说,能消费才是最重要的,就算拿到的数据不是最新的数据,消费者本身也可以进行尝试失败重试。总比为了追求数据的一致性而获取不到实例信息整个服务不可用要好。
    所以,对于服务注册来说,可用性比数据一致性更加的重要,选择AP。

分布式锁,选择AP还是CP?
  • 基于redis实现分布式锁
    为了解决数据库锁的无主从切换的问题,可以选择redis集群,或者是 sentinel 哨兵模式,实现主从故障转移,当master节点出现故障,哨兵会从slave中选取节点,重新变成新的master节点。
    哨兵模式故障转移是由sentinel集群进行监控判断,当maser出现异常即复制中止,重新推选新slave成为master,sentinel在重新进行选举并不在意主从数据是否复制完毕具备一致性。
    所以redis的复制模式是属于AP的模式。保证可用性,在主从复制中“主”有数据,但是可能“从”还没有数据,这个时候,一旦主挂掉或者网络抖动等各种原因,可能会切换到“从”节点,这个时候可能会导致两个业务县城同时获取得两把锁
    如果在社交发帖等场景下,我们并没有非常强的事务一致性问题,redis提供给我们高性能的AP模型是非常适合的,但如果是交易类型,对数据一致性非常敏感的场景,我们可能要寻在一种更加适合的 CP 模型

  • CP还是AP的分布式锁技术选型
    无论是redis,zk,例如redis的AP模型会限制很多使用场景,但它却拥有了几者中最高的性能,zookeeper的分布式锁要比redis可靠很多,但他繁琐的实现机制导致了它的性能不如redis,而且zk会随着集群的扩大而性能更加下降。
    简单来说,先了解业务场景,后进行技术选型。

参考:
https://www.ruanyifeng.com/blog/2018/07/cap.html
https://juejin.im/post/5d720e86f265da03cc08de74#heading-26
https://www.infoq.cn/article/cap-twelve-years-later-how-the-rules-have-changed
https://mwhittaker.github.io/blog/an_illustrated_proof_of_the_cap_theorem/

猜你喜欢

转载自blog.csdn.net/weiyi_world/article/details/107351700