背景

微服务的流行,使得现在基本都是分布式开发,也就是同一份代码会在多台机器上部署运行,此时若多台机器需要同步访问同一个资源(同一时间只能有一个节点机器在运行同一段代码),就需要使用到分布式锁。然而做好一个分布式锁并不容易

例如分布式锁的选择,你是选择Redis的主备集群(AP模型)还是选择ZK、etcd(CP模型)呢? ,如果不具备这些理论知识,我觉得是无法灵活选择且用好分布式锁的,不同业务场景有不同AP、CP模型的需求,例如为什么Nacos的配置中心使用CP模型,但注册中心却使用AP模型呢?

CAP理论

CAP理论是在分布式集群环境下讨论的,为什么分布式集群环境下会存在CAP问题呢?举个例子,假设我们后端存储服务使用Redis中间件,如果只部署一台Redis服务器,那么这台Redis如果挂了,整个存储服务都挂了,那么我们就想要提高它的可用性,怎么办呢?最简单的方法就是加机器:

可用性:我部署两台Redis机器在一个集群中(主备集群),如果此时有一台Redis挂了我客户端照样可以访问另一台Redis,保证了一定程度的可用性(保证允许一台Redis宕机) 同时这两台主备Redis需要时刻保持数据同步,这样在一台宕机之后另一台也能保持原有的查询服务:

一致性:如果我客户端在A节点的Redis设置了一个M值,然后A节点宕机,此时换B节点的Redis上,如果B节点正好同步了这个M值,就保证了一致性,但是有可能B节点的Redis还没来得及同步这个M值,我客户端在B节点读不到M值,这就存在了一致性问题 那什么是分区容错性呢(P)?

分区容错性:这两台主备Redis发生了分区(A、B节点互相连接不上),那就做不了数据同步了,但此时集群依然向外开放服务,此时集群具有分区容错性,如果发生分区,集群就不能对外服务,则集群不具有分区容错性(P特性) 分区容错性,代表当分布式节点发生分区(A、B节点互相连接不上)此时的分布式系统是否还提供服务(是否容错),如果没有了P,代表发生分区之后整个分布式集群不能使用,这显然是不行的。下面看看保证P与不保证P的集群是什么样的:

如果没了P,理论上集群不容许任何一个节点发生分区,当没有分区发生时确实可以保证AC(谁能保证集群系统的节点百分百不会出问题呢?能保证也不需要讨论AC问题了),当发生分区时整个集群不可用,没有现实意义(如图1) 如果保证P,说明集群就只能从A和C选择其一(如图2) 综上所述,所以做一个分布式系统,P一定需要保证,那么我们的焦点就在AP与CP的模型去选择

半数问题

  • 事务操作的性能瓶颈:由于需要保证半数节点保存了数据,则增删改数据的性能取决于半数节点的性能(5个节点的写入性能则取决于3个节点(包括自己)的写入速度)和与半数节点通信的网络开销。这意味着,集群节点越多(如果想要高可用,那就上多节点),写入性能有可能会越差(试想,上100个节点,每次写入都要发给50个节点确保写入成功,有50次网络开销,不过数据复制过程是并行的,木桶效应总耗时取决于半数中最慢的那个节点,不过节点越多,木桶短板出现的概率也就越大,所以这里说写入性能是可能变差的)

  • 基本可用:集群由于需要半数提交才能成功写入数据,所以如果5个节点宕机2个集群是可用的,若宕机3个,由于得不到半数的提交,集群就会不可用,从这点上来看达到了基本可用的特性

  • 最终一致性 or 强一致性(线性一致性):

如果我们仅仅只是写入一个值,从写入角度来说那就是强一致性(因为都在Leader处理,Raft与Zab都支持顺序一致性,即为你刚刚Set M = 1,又Set M = 2,顺序如果反了那结果会变成 M = 1,由于都在同一个Leader节点做,节点维护一个队列即可保持顺序),所以下面更多的是对于读数据的一致性讨论

  1. 最终一致性:这取决于具体的实现,若是最终一致性,那么Follower就开放读能力,这么做有个优点,整个集群的读能力会随着节点个数得到提升,但有个缺点,由于半数提交的规则,有可能上一秒你写入成功了一个值,下一秒在集群另外某个节点中读不到这个值(可能还没同步到),但最终会在一个时间点此值被同步,也就是最终一定会达到一致性
  2. 强一致性(线性一致性):如果读能力只在Leader节点开放,也就是读我也只能在Leader上读,那么此时的一致性是极强的,随着上一秒的设置值,下一秒一定可以查到刚刚设置的值,缺点就是读性能得不到扩展,局限于Leader单点性能的问题(读写都在一台节点上操作)

以上讨论了CP模型,对一致性要求比较高的系统比较适用,可以看出来,这种分布式协议有性能的局限性,因为他牺牲了一定的客户端响应延迟来确保一致性,而且仅仅保证了半数的基本可用性。在延时要求高、高并发场景下这种模型或许并不适用,此时可以考虑AP模型提高响应延迟。

如果是AP模型,相比于一致性协议会简单一些,因为他只需要保证可用性,加集群节点即可,而数据丢失、不一致的情况只会在宕机的那一时刻发生,丢失、不一致的也只是那一时刻的数据,例如Redis的主从集群架构,主Redis节点只需要异步、定时去同步数据,在写入时只需要一个节点确认写入即可返回,延时比一致性协议低,由于Redis在使用上大部分场景都用在缓存,快是他的设计目标,偶尔丢几条或者不一致几条缓存数据并不影响场景(关于Redis主从的AP模型的讨论更详细的在这里)。

回答开篇

为什么Nacos配置中心要使用CP模型?

答:CP模型牺牲了一定的延时去换取数据的强一致,但应用的外部化配置并不要求配置成功的延迟要多低,更多的是要保证配置信息的一致性(我配置信息 A = 1,不要给我弄丢了或者等下查 A 不等于 1,造成 X 服务 A = 1,Y 服务 A = 0 情况发生,这些都是数据不一致,这肯定是不行的),所以在这种配置场景下是十分适合做CP模型的

为什么Nacos注册中心要使用AP模型?

答:这个问题也可以转换成为什么注册中心要使用AP模型。因为可用性比一致性更加重要,可用性体现在两个方面,第一是容许集群宕机数量,AP模型中集群全部机器宕机才会造成不可用,CP模型只容许一半的机器宕机。第二是分区是否可写,例如A机房和B机房发生分区,无法进行网络调用,在CP模型下部署在A机房的服务就无法部署,因为A机房无法与B机房通讯,所以无法写入IP地址,在AP模型下,可以去中心化,就像Eureka那样就算发生分区每台机器还都能写入,集群间无Master,而是互相复制各自的服务IP信息,这样,在分区时依然是支持写入的。不可用会造成微服务间调用拿不到IP地址,进而业务崩盘,所以不可用是不可接受的,不一致会造成不同微服务拿到的IP地址不一样,会造成负载不均衡,这是可以接受的。

AP or CP

CP模型