AlexiaChen / AlexiaChen.github.io

My Blog https://github.com/AlexiaChen/AlexiaChen.github.io/issues
87 stars 11 forks source link

架构设计-----分布式CAP理论 #80

Open AlexiaChen opened 4 years ago

AlexiaChen commented 4 years ago

分布式理论----CAP

CAP理论

CAP定理是由UC Berkeley的Brewer教授在2000年的ACM PODC上提出的一个猜想,然后由MIT的某两个教授继续完善并证明了这个猜想,这样就让这个猜想成为了分布式领域的一个定理。如果你要设计或者是搭建一个分布式系统,那么CAP理论你需要懂一些的。

CAP猜想被提出来的时候,并没有详细定义Consistency,Availability,Partition Tolerance三个概念的明确含义,之后才有人慢慢完善概念,所以你去网络上搜索,解释在细节上还是有3一点点差别的。

下面会摘录国外一位大神对CAP理论的渐进式理解,他也是慢慢来的。

简单粗暴版本:

科学严谨版本:

这两个版本有点差异,主要有两点:

下面单独分开讲解CAP的3个设计约束。

一致性(Consistency)

简单粗暴版本:

科学严谨版本:

两个版本差异有以下2点:

可用性

简单粗暴版本:

科学严谨版本:

两个版本差异:

分区容忍性

简单粗暴版本:

科学严谨版本:

差异:

CAP理论的应用

感觉上CAP理论定义是三个约束中只能取两个是不是?然而,这是一种误导,不是任意3选2,因为放到物理现实中来看,对于分布式系统而言,你会发现不得不选择P(分区容忍性),因为网络本身无法做到100%可靠,网络分区是个必然现象。所以讨论CAP理论的时候,满足P是个大前提,接下来你要做的就是在C和A中选择一个了。分布式系统理论上是不可能选择CA架构的,只能要么是CP,要不是AP。

CP系统

考虑一个分布式系统中两个节点A和B,为了保证强一致性,当发生分区现象后,节点A的数据已经更新了(状态x -> 状态y),但是由于节点A和B之间的网络发生故障了,节点A的状态y无法同步复制到节点B,节点B的状态还是x。这时候客户端访问节点B,节点B需要返回Error(因为系统要求强一致,所以必须按系统错误处理),提示客户端“系统现在发生了错误”,这种处理方式违背了CAP中的A的要求,分区发生时,系统居然返回Error,A的要求是要返回合理的结果,结果可以不正确,但是不能是系统错误或超时!!。因此CAP三者只能满足CP。

AP系统

考虑一个分布式系统中两个节点A和B,为了保证可用性,当发生分区现象后,节点A的数据已经更新了(状态x -> 状态y),但是由于节点A和B之间的网络发生故障了,节点A的状态y无法同步复制到节点B,节点B的状态还是x。这时候客户端访问节点B,节点B将当前拥有的状态数据x返回给客户端了(因为不要求强一致了,可以放宽要求),而实际上当前最新的状态数据已经是y了,这就不满足一致性中的强一致的要求(无论是简单版本还是严谨版本的解释)。因此CAP三者中只能满足AP。

CAP理论的相关细节

CAP理论的优点在于清晰简洁,易于理解,但是缺点就是过于理论化,缺乏工程上的实践指导意义,如果你需要在实践中应用CAP理论,你没注意到有些关键细节的话,你可能会发现方案难以落地。

每个分布式系统不可能单单只处理一种类型的数据,有的数据策略需要选择CP,有的数据需要选择AP,所以做设计的时候不要从整个系统的角度去选择CP还是AP,不然会发现顾此失彼,无论怎么做都有问题。

至于选择CP还是AP,看你对某种类型的数据的一致性要求了,有些数据真不需要保证强一致性。比如微信头像,即使我最新的头像换成刘亦菲了,其他人还是可以看到我之前的旧头像的。没必要见到最新头像,这种数据还是选择AP吧,至于C我不保证强一致,但我保证最终一致性嘛(最终一致性就不是C了)。

所以总结一句,看数据类型,看场景。

这个是隐藏的比较深的假设,即使C强调的是强一致,但是在物理世界中,网络传输是有时延的,事务提交完成后,数据并不能瞬间复制到所有节点,至于时延多少,这个取决于网络布局了,是否在同一个机房,是否在同一个城市,是否在不同的城市,这个时延多少,如果是架构师,确实要做到心中有个数,Jeff Dean写过相关的文章我总结了发出来了,在这里https://github.com/AlexiaChen/AlexiaChen.github.io/issues/76。

另外,不要小看这毫秒级别的时延,这个微小的时间窗口内的不一致,对于一些金融支付相关的的业务场景下是致命的。那就意味着这样的场景不能分布式架构了吗?就完蛋了吗? 显然不是的,既然业务上要求强一致,但是现实情况又无法达到那样100%的强一致,大不了就不考虑P了呗,只选择CA,不把网络考虑进来了,我单节点写入,其他节点只做数据备份,不做多节点写入。

咦?好奇的人说,那这样算个毛分布式架构啊? 其实动点脑子还是可以的,如果要多节点写入,那么这几个节点需要各自负责一段指定范围的数据,比如节点A负责处理用户ID为0到100的数据并备份101-200的数据,节点B负责用户ID为101-200的数据并备份0-100的用户数据。但这样的设计有个缺陷就是某个节点故障了,这个节点上的用户就无法进行读写操作了,业务不能进行。但是站在整体系统来看,这种设计降低了影响范围。这就是为什么在挖掘机挖断光缆以后,支付宝只有一部分用户出现业务异常,而不是所有的用户。你看,连阿里也无法完美解决这样的问题。

标题什么意思呢?P是指,当网络分区现象发生时,系统能继续提供业务服务。但是你仔细想想,分区现象不是一直都在发生的好吗,分区虽说是个必然现象,但是往往发生时间并不长。也就是说,大部分时间下,大部分正常情况下(废话,如果网络分区一年时间有11个月的时间都发生问题,那么网络直接不可用了,咱啥也别接着讨论了),网络分区没有发生的,就意味着这个时候,系统可以不用考虑P,可以只考虑CA。所以设计分布式架构就有以下逻辑:

// 分布式系统不停的run
while(true)
{
    if(网络分区发生 == true)
    {
        // 设计者根据业务场景决策
        if(数据是否需要保证一致性 == true)
        {
            CP();
        }
        else
        {
            AP();
        }
    }
    else
    {
        CA();
    }
}

上面的逻辑简而言之就是,分布式系统既要考虑分区发生时选择CP还是AP,也要考虑分区没发生时如何保证CA。

放弃这个词在原始的CAP理论里面是有误导意义的,确实有些时候我感觉也是莫名其妙。在分区过程中,无法同时保证C和A,并不意味咱啥也不管,因为系统在整个运行周期中,大部分时间里分区是没有发生的,发生分区现象的时间并不长,例如99.99%可用性(俗称4个9)的系统,一年运行下来,不可用的时间只有50分钟,如果是5个9的高可用系统,一年运行下来,不可用的时间只有5分钟。分区期间放弃C或A,并不意味着永远放弃C和A,现实情况下,可以在分区期间进行一些操作,从而让分区现象消失或者解决后,系统能够重新达到CA的状态。

当然,这种操作一般是写本地日志了,当分区发生后写日志,当分区故障恢复后,把本地日志里面的记录再通过网络同步过去,这样又回到CA状态了。

ACID与BASE

当谈到数据一致性的时候,CAP,ACID,BASE难免都会被拿出来比较,原因在于这三者都和数据一致性多少有点关系,如果不仔细研究,可能会陷入一头雾水的状态。

ACID

ACID是数据库管理系统(DBMS)为了保证事务的正确性而提出来的一个理论,所以谈到ACID,我们一般是指数据库事务(transaction)。

这个概念跟内存上的原子操作概念差不多,对于一个事务,要不啥也没干,要不就全干完。发生错误就回滚。回滚是实现原子性的一个手段。

事务开始(begin)之前和事务结束(commit)之后, 数据库的完整性没被破坏。

数据库允许多个并发事务同时对数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉而导致的数据不一致。事务隔离由不同级别: 读未提交(read uncommitted), 读提交(read committed), 可重复读(repeatable read) 和串行化(Serializable)。每个级别对应不同的性能,当然,串行化性能是最低的,相当于没有事务并发,都得排队。

事务结束后(committed),对数据的修改就是永久的,即便系统故障也不会丢失。

所以,你看到了,ACID中的A和CAP中的A根本不是一个概念,C虽然都是说一致性,但是含义也不一样,ACID中的C是指数据库的数据完整性(可能通过Write-ahead log实现),CAP中的C是指分布式节点中的数据或状态的一致性。再所以,ACID跟CAP完全没有可比性,不是同一层面的概念。

BASE

其实你也看到了,CAP太理想,太理论了,对于设计分布式系统的工程实践指导意义不大,C是100%强一致,A是100%可用性,都是完美100%。实际上哪有这样的系统,不存在的,要对现实做妥协,所以BASE理论作为CAP的外延补充,应运而生了。它是CA权衡的结果,更是工程实践的总结。

BASE中的BA是Basically Available(基本可用)的缩写,S是Soft state(软状态),E是Eventally Consistency(最终一致性)。其核心思想就是如果你的系统无法做到强一致性,那么系统结合自身的业务特点可以采用合适的方式达到最终一致性。

分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。至于哪些业务可以损失可用性,哪些不可以,这个要看架构师怎样权衡了。这里可以根据一个简单的原则就是,越跟金钱相关的业务,越不能损失可用性,越不相关的的业务一般就没啥大不了的。

允许系统存在中间状态,而该中间状态不会影响系统整体可用性。这里的中间状态就是CAP理论中的数据不一致的这段时间窗口的状态。

系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。

放宽了CAP中的C强一致的追求。给予了一定时间内的缓冲期,但是这个一定时间是根据数据类型不同而不同的。有些时间可以长些,有些时间不能太长。

100%完美的CP,CA场景都是不存在的,因为有时延。AP方案中牺牲C,那只是分区期间,而不是永远放弃一致性,等到分区恢复后,系统应该通过某种手段达到最终一致性。

当然,由于业界的发展,现在很多系统已经开始选择CP了,因为A虽然达不到100%可用性,但是可以让A达到一定程度的高可用(HA),所以选择CP的系统,不代表没有可用性啊,这点要明白。