分布式系统里,最重要的事情,就是如何选择或设计适合的算法,解决一致性和可用性相关的问题了。InfluxDB企业版本的技术壁垒就是以分布式算法为核心的分布式集群能力。有些人在接入性能敏感的场景,该使用反熵(Anti-Entropy)算法的时候,却用了 Raft 算法,使得集群性能约等同于单机。
CAP 理论对分布式系统的特性做了高度抽象,形成了三个指标:
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition Tolerance)
一致性说的是客户端的每次读操作,不管访问哪个节点,要么读到的都是同一份最新写入的数据,要么读取失败。一致性这个指标,描述的是分布式系统非常重要的一个特性,强调的是数据正确。也就是说,对客户端而言,每次读都能读取到最新写入的数据。
可用性说的是任何来自客户端的请求,不管访问哪个非故障节点,都能得到响应数据,但不保证是同一份最新数据。我尽力给你返回数据,不会不响应你,但是我不保证每个节点给你的数据都是最新的。这个指标强调的是服务可用,但不保证数据正确。
分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,节点间的分区故障是必然发生的,所以我要提醒你的是,在分布式系统中分区容错性是必须要考虑的。
只要有网络交互就一定会有延迟和数据丢失,而这种状况我们必须接受,还必须保证系统不能挂掉。所以就像我上面提到的,节点间的分区故障是必然发生的。也就是说,分区容错性(P)是前提,是必须要保证的。
只剩下一致性(C)和可用性(A)可以选择了:要么选择一致性,保证数据正确;要么选择可用性,保证服务可用。那么 CP 和 AP 的含义是什么呢?
- 当选择了一致性(C)的时候,一定会读到最新的数据,不会读到旧数据,但如果因为消息丢失、延迟过高发生了网络分区,那么这个时候,当集群节点接收到来自客户端的读请求时,为了不破坏一致性,可能会因为无法响应最新数据,而返回出错信息。
- 当选择了可用性(A)的时候,系统将始终处理客户端的查询,返回特定信息,如果发生了网络分区,一些节点将无法返回最新的特定信息,它们将返回自己当前的相对新的信息。
在不存在网络分区的情况下,也就是分布式系统正常运行时(这也是系统在绝大部分时候所处的状态),就是说在不需要 P 时,C 和 A 能够同时保证。只有当发生分区故障的时候,也就是说需要 P 时,才会在 C 和 A 之间做出选择。而且如果读操作会读到旧数据,影响到了系统运行或业务运行(也就是说会有负面的影响),推荐选择 C,否则选 A。
常见系统 CAP 属性
CA 模型
在分布式系统中不存在 CA 模型,单机版MySQL是典型的 CA 模型,如果是主备 MySQL 或集群部署,就必须考虑 P。
CP 模型
采用 CP 部署的分布式系统,舍弃可用性,一定会读到最新数据,不会读到旧数据。一旦因为消息丢失、延迟过高发生网络分区,就会影响业务的可用性,例如基于 Raft 的强一致性系统,在发生网络分区时无法执行读操作和写操作。典型应用如 Zookeeper、Etcd、Consul 和 Hbase
如任何时刻对zookeeper的访问请求能得到一致性的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务的可用性。从实际情况来分析,在使用zookeeper获取服务列表时,如果zk正在选举或者zk集群中半数以上的机器不可用,那么将无法获取数据。所以说,zk不能保证服务可用性。
zookeeper的分布式锁要比redis可靠很多,但他繁琐的实现机制导致了它的性能不如redis,而且zk会随着集群的扩大而性能更加下降。
AP 模型
舍弃一致性,实现服务高可用。用户访问系统都能得到响应数据,但可能读到旧数据。典型应用如Redis、eureka、Cassandra 和 DynamoDB、云搜。
eureka保证AP,eureka在设计时优先保证可用性,每一个节点都是平等的,一部分节点挂掉不会影响到正常节点的工作,不会出现类似zk的选举leader的过程,客户端发现向某个节点注册或连接失败,会自动切换到其他的节点,只要有一台eureka存在,就可以保证整个服务处在可用状态,只不过有可能这个服务上的信息并不是最新的信息。
Elasticsearch
Paxos
Es 采用Gossip算法
ZAB
Raft