这里不想赘述相关的概念,只根据我的理解和总结来进行阐述:
C(consistency):强一致性,也叫线性一致性,指任何时候访问系统时,对于同一份数据,要么都查不到,要么都查能到,如果有多个节点的话,每个节点中的数据都是一模一样的;
A(Availability):高可用,本质就是数据冗余,一份一模一样的数据,在所有节点中都存一份拷贝,如果出现节点故障,只要有另外一个节点可以提供服务,该数据就可以被访问到;
P(Partition-tolerance):分区可扩展/容错:一份数据可以保存到多台机器上,且当出现数据节点彼此间网络不通的情况下,系统依然可以使用;
根据CAP理论,以上3种属性,在任何分布式系统中,都最多只能同时满足其中的2个。
这里需要澄清的几个常见理解误区:
解释一下:
第1点:
比如只有一个master和若干个slaver的分布式系统来说。如HDFS,从集群架构上来看,只有一个namenode和若干个DataNode,namenode是存在单点故障的,而并非高可用。
但是这里我们谈论的CAP针对的是slaver节点中的数据,只要其数据有备份(默认3份),那么该系统就符合高可用的特点,跟master没有关系,这一点要切记,不要混淆;
第2点:
对于一个分布式系统来说,CAP只能同时满足2个指的是在【苛刻】条件下的要求,而非【苛刻】条件下,其实是3个都是可以满足的。
拿Elasticsearch这个分布式存储来说,它在设计时遵循CAP中的AP。
首先满足P,这一点毋庸置疑(几乎所有分布式存储系统都首要考虑P,一份数据可以写到多个节点中,且如果系统出现网络分区导致无法通信,系统一样可以向外提供服务);
其次满足A,因为系统存在备份(可以设置多个冗余分片),且都能同时对外提供读取服务器;
最后再来看C,为什么不满足,原因在于系统的数据都是先全部写到主分片,写完后再把数据同步到冗余分片中,这一过程存在时间差,且在这个过程中所有的数据分片都是可以对外提供服务的,因此无法满足强一致性,即同一时刻对不同数据分片请求数据,获取到的结果会出现不一致。
但是,当主分片的数据全部同步到冗余分片中之后,是不是就一致了呢。因此,这个C最终是满足的,所以Elasticsearch在非【苛刻】条件是可以同时满足CAP的。
第3点:
对于很多分布式系统来说,通过修改一些系统参数是可以在C、A、P之间做动态取舍的。
以HDFS为例,它原本是一个遵循AP的分布式系统,但是我们可以通过控制参数dfs.replication.min来改变这一事实。
dfs.replication.min参数用来控制客户端的最小写入副本数,默认为1。也就是默认情况下,客户端在数据写入时,只要保证其中一个DataNode写入成功,就算成功,后续的数据复制属于异步进行,默认保证AP。
比如HDFS默认的复制因子为3,默认情况下当客户端写入数据到其中一个datanode成功后,客户端即刻返回,剩下的2份数据会由系统的后台线程将其复制到另外2个DataNode中,这里是舍弃了C特性的。
但是如果我现在将dfs.replication.min改为3,那么客户端会一直等待3个DataNode都将这份数据写入成功后才返回,在这个过程中,该份数据是不会对外服务的(即不会commit),这个时候就保证了C特性,而放弃了P,因为这个过程中如果某个DataNode出现网络分区(无法通信),系统会触发选择新DataNode的机制,进而来确保网络分区问题快速解决,否则写入的这份数据是没法commit的,也就不能对外提供服务。
在大家接触的大多数分布式系统中,设计之初都默认会确保P,然后再在C和A之间做取舍,但是有一个系统比较例外,那就是kafka。
kafka不保证P?纳尼?
先来看看kafka的设计者Jun Rao怎么说的。
All distributed systems must make trade-offs between guaranteeing consistency, availability, and partition tolerance. Our goal was to support replication in a Kafka cluster within a single datacenter, where network partitioning is rare, so our design focuses on maintaining highly available and strongly consistent replicas.”
看到没,kafka的设计注重的是A和C属性,抛弃了P,可能会颠覆很多人的一般性认知。
虽然kafka在设计时没有考虑优先保证P,但是并不代表kafka的P属性不存在。
解读:
kafka跟其他分布式系统的特殊之处在于,整个集群架构设计中没有master和slaver之分,但是在其数据存储内部,每个topic中的每个partition却分为leader和follower,和写每个partition的leader和follower构成了一个小系统也就是上面提到的datacenter,所以在kafka内部,存在无数个这样的datacenter。
按照kafka设计者的意图,当数据写入partition中的leader和所有follower之后,客户端才返回,数据才提供服务(即ack=-1的情况),这个时候只要系统设置的冗余因子大于1个,像上文中举的HDFS例子一样,满足了CA,舍弃了P的考虑,一但出现网络分区(datacenter间无法通信),系统就会触发补救机制,来确保网络分区问题很快解决,否则数据不能commit,也就不能对外提供服务。
对于C:任何时候,当一个客户端去访问系统时候,永远不存在数据不一致的情况,要么找不到,找到就一模一样;
对于A:任何时候,都不存在单点故障,如果有一个数据节点无法提供服务(出现故障),必然存在至少一个它的备份可以提供一样的服务,且这个切换不需要系统做出额外的动作(如热切换);
对于P:这个比较难理解一点。任何时候,当系统出现网络分区(可能是任何故障),导致节点彼此无法通信时,但是不影响客户端仍然可以访问系统,只是不能保证访问到你需要的目标数据,且这期间系统不会做出额外的动作来保证这一点;
还是拿刚才举例过的HDFS、ELasticsearch和kafka来论证一下:
原文作者:安瑞哥是码农;如有侵权,请联系管理员删除。