在分布式系统中最重要的抽象概念之一是共识, 即在网络、进程故障等情况下,让所有非故障节点在某一件事情上达成一致.那么在实现共识的过程中,我们就需要在理论与实践中去发现哪些可行, 哪些不可行.对此我们需要了解什么是分布式一致性, 它和我们构建一个高可用系统架构有什么关联? 同样今天来谈谈自己的思考.
高可用原理本质
一谈到高可用架构设计, 想必我们都会想到采用“冗余”来实现高可用.即服务不可用,增加冗余服务;节点不可用,增加冗余节点;数据不可用,增加冗余数据副本.这个时候我们的系统将由单体服务由于引入冗余机制组成“自己”的集群,即:
从上述集群架构中我们仅看到了多份相同的服务、节点和数据,但是要实现高可用,还需要一个关键能力,即自动故障转移.
什么是自动故障转移,在上述的服务/节点集群架构中, 我们试想下如果此时客户端请求集群服务,那么引入冗余机制之后, 起初我们的架构如下:
从上述我们也很容看到, Client是没有感知到下游故障节点,还是会将请求转发到对应的故障节点中.也就是我们Client如果能够感知到下游节点的变化,那么我们Client就能够进行决策,不将请求转发到故障节点上,因此上述自动故障转移其中一个关键因素就是状态感知并决策的能力.
因此我们实现高可用架构的关键在于增加冗余并实现自动故障转移机制,而实现自动故障转移的核心是状态感知并进行决策的能力,也就是说我们集群中的服务、节点以及数据需要通过协调彼此状态来进行决策.
分布式一致性与高可用状态决策
既然我们知道自动故障转移是依赖我们协调对应的节点来进行决策,在前面我们讲述过共识算法是一种通用有效保障的抽象,因此我们这里的共识算法就是实现一个状态检测与决策,那么基于上述的基础上,我们在冗余的集群引入共识算法来实现高可用,这个时候我们的架构就会出现以下的演变方式,即:
上述状态决策是我们高可用架构状态决策中常用的两种方式, 即独裁式以及民主状态决策, 内部都是依托我们的共识算法来实现.比如我们Redis的Sentinel哨兵机制就是独裁式决策, 而ZK集群则是民主式决策.
因此这个时候我们再来理解分布式一致性的含义可能会更容易理解, 分布式一致性就是协调各个节点的状态以达成状态决策的一致性,而这个状态主要有两种, 其一是我们现在看到实现高可用需要感知到节点状态并进行状态的决策; 其二是对于数据存储高可用服务, 还包含另外一个状态, 即数据的一致性, 从而产生CAP以及BASE理论两大框架来辅助我们如何就数据一致性进行架构设计与决策.接下来我们再来看下数据层面的一致性设计.
强一致性与CAP定理
在存储高可用架构中, 除了节点状态的一致性, 同时还存在数据的一致性问题,那么数据一致性是怎么产生的呢?
这里我引用《设计数据密集系统》一书的一个例子, 假如先Alice以及Bob坐在同一个房间使用不同的手机查看足球世界杯,这个时候世界公布比分不久, Alice刷新了页面看到最新的公布结果看到了公布的获胜方, 然后告诉了Bob; 然而Bob刷新了自己手机的页面却一直没有看到最新的结果,这是为什么呢?
原来Alice的请求转发的数据follow1的副本, 而Bob的请求却给转发到了Follow2的副本,可以看到Follow2的副由于存在网络延迟导致数据复制滞后, 而这段时间Bob看到的结果还是旧的一份数据, 即:
从上述可以看到我们设计数据存储的高可用代价就是存在数据的一致性, 即leader节点将通过指定的复制方式发送复制数据格式到对应的数据副本节点上,而数据一致性的产生正是由于复制过程中存在网络延迟导致数据不一致.
那么怎么解决这个问题呢? 还是一样利用共识算法来实现数据的一致性.我们可以看到上述导致数据读取不一致性根本原因是啥? 就是客户端请求不知道当前数据存储存储到底哪一个数据副本节点是最新的, 这个时候我们同样引入共识算法来协调存储集群内部当前最新数据状态的副本有哪些并达成共识,这个时候请求过来的时候先通过共识算法协调请求应该落到哪个节点能够读取最新的数据.
其实与前面处理节点的方式是类似的,只不过这里的192.168.1.21的节点并非是不可用节点,而是数据不是最新节点,如果采用左边的架构方式,那么当Client请求192.168.1.21节点的时候就会将请求转发到其他最新的节点上; 而如果是引入协调者, 那么就不会将请求转发到192.168.1.21节点上.这个时候就是我们所说的线性一致性,即强一致性模型.
在我们的CAP定理中的C就是强一致性模型,相对于分布式一致性,它的范围更小,它主要是强调在非数据共享架构且存在数据复制的架构下,如何协调数据最新状态的一致性,即读己之所写的一致性模型.CAP是建立在FLP定理无网络延迟的假设条件下, 阐述一致性、可用性以及分区容忍只能满足其中两个属性.
从上述可以看出CAP忽略了实际网络分区的存在,而且如果我们用CAP来描述系统, 其实是不正确的,因为一个系统的数据可以是满足AP, 也可以是满足CP, CAP关注的是单个数据,并且是在非数据共享以及数据复制架构前提下去讨论我们数据的一致性,因此我们CAP的不可能三角以及对应的限定条件如下:
最终一致性与BASE理论
从上述的实现我们看到192.168.1.21这个节点其实并非故障,但其实就是不能进行请求处理,因为我们要保证读己之所写的线性一致性而牺牲这个节点对外服务并存在浪费资源情况. 如果期间经过了时间T,Bob重新刷新页面发现数据更新了,那么这个时候Alice以及Bob看到的页面结果是一致,只是存在时间差的问题, 一旦我们能够容忍这种故障,那么192.168.1.21这个节点也将会加入服务提供列表中支持处理请求, 这个时候我们服务的可用性也将会提升, 即由原来的2台提升至3台并对外提供服务,同样也提升了资源利用率.
像上述的数据表现是允许短时间内出现数据不一致,即混沌状态,但是经过一段时间之后,我们的数据最终表现为一致的,我们称之最终一致性.对于最终一致性在分布式系统中存在一个BASE理论,是由eBay架构师Dan Pritchett于2008年提出,作为对CAP理论中的AP场景实践的补充,强调通过弱化一致性实现系统的高可用,即:
BASE理论含义:BASE是“Basically Available(基本可用)”、“Soft state(软状态)”和“Eventually consistent(最终一致性)”三个短语的缩写,由eBay架构师Dan Pritchett提出。核心思想即使无法做到强一致性, 但应用也能够采用合适的方式达到最终一致性.
从这里我们可以看出BASE理论是在我们CAP基础上进行属性弱化,由CAP中的强一致性弱化为最终一致性, 而可用性弱化为基本可用, 对此我们可以将CAP以及BASE理论进行对比如下:
总结
至此我们已经对共识与FLP定理、CAP定理以及BASE理论有了一个认知,这里我把之前的知识点串起来如下:
那么当我们要进行高可用架构的时候,如何运用上述来辅助我们进行架构落地呢? 这里我总结并画了张以供参考: