《深入分布式缓存》之“分布式理论:CAP是三选二吗?”

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 版权声明:本文为半吊子子全栈工匠(wireless_com,同公众号)原创文章,未经允许不得转载。 https://blog.csdn.net/wireless_com/article/details/79153643 CAP是什么?CAP理论,被戏称为[帽子理论]。
版权声明:本文为半吊子子全栈工匠(wireless_com,同公众号)原创文章,未经允许不得转载。 https://blog.csdn.net/wireless_com/article/details/79153643

CAP是什么?

CAP理论,被戏称为[帽子理论]。CAP理论由Eric Brewer在ACM研讨会上提出,而后CAP被奉为分布式领域的重要理论[1] 。

 

分布式系统的CAP理论:首先把分布式系统中的三个特性进行了如下归纳:

● 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

● 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

● 分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。(分区状态可以理解为部分机器不连通了,比如机器挂了,繁忙失去响应,单机房故障等)

Partition字面意思是网络分区,即因网络因素将系统分隔为多个单独的部分,有人可能会说,网络分区的情况发生概率非常小啊,是不是不用考虑P,保证CA就好。要理解P,我们看回CAP证明中P的定义:
In order to model partition tolerance, the network will be allowed to losearbitrarily many messages sent from one node to another.

网络分区的情况符合该定义,网络丢包的情况也符合以上定义,另外节点宕机,其他节点发往宕机节点的包也将丢失,这种情况同样符合定义。现实情况下我们面对的是一个不可靠的网络、有一定概率宕机的设备,这两个因素都会导致Partition,因而分布式系统实现中 P 是一个必须项,而不是可选项。

高可用、数据一致性是很多系统设计的目标,但是分区又是不可避免的事情。我们来看一看分别拥有CA、CP和AP的情况。

 

CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但其实分区不是你想不想的问题,而是始终会存在,CA系统基本上是单机系统,比如单机数据库。2PC是实现强一致性的具体手段。

 

from http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf

CP without A:如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。

from 同上

AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。

 

from 同上

 

CAP理论的证明

 

该理论由brewer提出,2年后就是2002年,Lynch与其他人证明了Brewer猜想,从而把CAP上升为一个定理。但是,它只是证明了CAP三者不可能同时满足,并没有证明任意二者都可满足的问题,所以,该证明被认为是一个收窄的结果。

Lynch的证明相对比较简单:采用反证法,如果三者可同时满足,则因为允许P的存在,一定存在Server之间的丢包,如此则不能保证C,证明简洁而严谨。

 

在该证明中,对CAP的定义进行了更明确的声明:

·        C:一致性被称为原子对象,任何的读写都应该看起来是“原子“的,或串行的。写后面的读一定能读到前面写的内容。所有的读写请求都好像被全局排序一样。

·        A:对任何非失败节点都应该在有限时间内给出请求的回应。(请求的可终止性)

·        P:允许节点之间丢失任意多的消息,当网络分区发生时,节点之间的消息可能会完全丢失。

对于CAP进一步的案例解释

2010年的这篇文章brewers-cap-theorem-on-distributed-systems/,用了三个例子来阐述CAP,分别是example1:单点的mysql;example2:两个mysql,但不同的mysql存储不同的数据子集,相当于sharding;example3:两个mysql,对A的一个insert操作,需要在B上执行成功才认为操作完成(类似复制集)。作者认为在example1和example2上 都能保证强一致性,但不能保证可用性;在example3这个例子,由于分区(partition)的存在,就需要在一致性与可用性之间权衡。对于复制而言,在很多场景下不追求强一致性。比如用户支付之后,交易记录落地了;但可能消费记录的消息同步存在延迟,比如消息阻塞了。在金融业务中,采取类似两地三中心架构,往往可能采取本地数据和异地机房数据同时写成功再返回的方式。这样付出了性能的损耗,响应时间变长。但发生机房故障后,能确保数据是完全可以读写的,保障了一致性。

CAP理论澄清

[CAP理论十二年回顾:"规则"变了]一文首发于 Computer 杂志,后由InfoQ和IEEE联合呈现,非常精彩[2],文章表达了几个观点。

 

“三选二”是一个伪命题

 

不是为了P(分区容忍性),要在A和C之间选择一个。分区很少出现,CAP在大多数时候允许完美的C和A。但当分区存在或可感知其影响的情况下,就要预备一种策略去探知分区并显式处理其影响。这样的策略应分为三个步骤:探知分区发生,进入显式的分区模式以限制某些操作,启动恢复过程以恢复数据一致性并补偿分区期间发生的错误。

“一致性的作用范围”其实反映了这样一种观念,即在一定的边界内状态是一致的,但超出了边界就无从谈起。比如在一个主分区内可以保证完备的一致性和可用性,而在分区外服务是不可用的。Paxos算法和原子性多播(atomic multicast)系统一般符合这样的场景。像Google的一般做法是将主分区归属在单个数据中心里面,然后交给Paxos算法去解决跨区域的问题,一方面保证全局协商一致(global consensus)如Chubby,一方面实现高可用的持久性存储如Megastore。

 

ACID、BASE、CAP

 

ACID和BASE 这两个术语都好记有余而精确不足,出现较晚的BASE硬凑的感觉更明显,它是“Basically Available, Softstate, Eventually consistent(基本可用、软状态、最终一致性)”的首字母缩写。其中的软状态和最终一致性这两种技巧擅于对付存在分区的场合,并因此提高了可用性。

CAP与ACID的关系更复杂一些,也因此引起更多误解。其中一个原因是ACID的C和A字母所代表的概念不同于CAP的C和A。还有一个原因是选择可用性只部分地影响ACID约束。

 

进一步看[分区]之后

 

用一下这张图[引用自 link 2],在状态S的时候是非分区状态,而分区模式则演化出来了S1,S2,那么问题来了,分区恢复之后,状态究竟是多少呢?有几种解决方案。

 

分区恢复策略:可交换多副本数据类型 

注意,能支持此类处理的场景是有限的。

riak_dt 就是这样一种保障最终一致性实现的数据结构,它分为Operation-based CRDTs、State-based CRDTs 2种形态。

riak_dt   link参见 link [3]。

State-based CRDTs

State-based CRDTs are called convergent replicated data types,or CvRDTs. In contrast to CmRDTs, CvRDTs send their full localstate to other replicas. CvRDTs have the following local interface:

·      query - reads the state of the replica, with no sideeffects

·      update - writes to the replica state in accordance withcertain restrictions

·      merge - merges local state with the state of some remotereplica

The merge function must be commutative, associative, andidempotent. It provides a join for any pair of replica states, so theset of all states forms asemilattice. The update functionmust monotonically increase the internal state, according to thesame partial order rules as the semilattice.

from:https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type

 分区恢复策略:回放合并

在分区恢复过程中,设计师必须解决两个问题:

  • 分区两侧的状态最终必须保持一致,
  • 并且必须补偿分区期间产生的错误。

如上图所示,对于分区恢复的状态S*可以通过未分区时的状态S为起点,然后按顺序[回放]相应的变化事件[以特定方式推进分区两侧的一系列操作,并在过程中一直保持一致的状态。]。Bayou[link 4]就是这个实现机制,它会回滚数据库到正确的时刻并按无歧义的、确定性的顺序重新执行所有的操作,最终使所有的节点达到相同的状态。

 

对于有冲突的情况,比如版本管理软件cvs,存在人工介入、消除冲突的处理策略。

 

 有限制处理

 

有限制处理:[link 2]提的自动柜员机补偿问题,比较形象说明了这个问题。此问题本质上是可用性和一致性的折衷处理。

 

以自动柜员机(ATM)的设计来说,强一致性看似符合逻辑的选择,但现实情况是可用性远比一致性重要。理由很简单:高可用性意味着高收入。不管怎么样,讨论如何补偿分区期间被破坏的不变性约束,ATM的设计很适合作为例子。

ATM的基本操作是存款、取款、查看余额。关键的不变性约束是余额应大于或等于零。因为只有取款操作会触犯这项不变性约束,也就只有取款操作将受到特别对待,其他两种操作随时都可以执行。

ATM系统设计师可以选择在分区期间禁止取款操作,因为在那段时间里没办法知道真实的余额,当然这样会损害可用性。现代ATM的做法正相反,在stand-in模式下(即分区模式),ATM限制净取款额不得高于k,比如k为$200。低于限额的时候,取款完全正常;当超过限额的时候,系统拒绝取款操作。这样,ATM成功将可用性限制在一个合理的水平上,既允许取款操作,又限制了风险。

 

分区结束的时候,必须有一些措施来恢复一致性和补偿分区期间系统所造成的错误。状态的恢复比较简单,因为操作都是符合交换率的,补偿就要分几种情况去考虑。最后的余额低于零违反了不变性约束。由于ATM已经把钱吐出去了,错误成了外部实在。银行的补偿办法是收取透支费并指望顾客偿还。因为风险已经受到限制,问题并不严重。还有一种情况是分区期间的某一刻余额已经小于零(但ATM不知道),此时一笔存款重新将余额变为正的。银行可以追溯产生透支费,也可以因为顾客已经缴付而忽略该违反情况。

 

总而言之,因为通信延迟的存在,银行系统不依靠一致性来保证正确性,而更多地依靠审计和补偿。“空头支票诈骗”也是类似的例子,顾客赶在多家分行对账之前分别取出钱来然后逃跑。透支的错误过后才会被发现,对错误的补偿也许体现为法律行动的形式。

 

此前,中行IBM大型机宕机,系统没有第一时间切换到热备或者异地容灾上,直接影响中行的信用卡支付相关业务,直到4小时之后才恢复服务。有对应的的原因包括日常演练等问题,等更重要的是在[可用性和一致性]之间选择了一致性,4H之后提供服务,备库仍然主要起数据备份的作用。

 

有限制处理方案是需要冒险滴,为了保障可用性,无法保障数据100%精确,可以折衷提供部分有损服务。比如取款根据信用是不是能限制一定金额,而存款是Ok的等等。大额对公业务也可以采取具体办法,当然这将在精细化管理服务能力及配套能力上付出更多的IT成本。

有关中行IBM大型机宕机的报道link:

http://digi.tech.qq.com/zt2013/syibm/

http://www.infoq.com/cn/news/2013/04/BOC-Downtime/

 

超越CAP?

 

Nathan Marz:How to beat theCAP theorem

 

2011年11月Twitter的首席工程师Nathan Marz写了一篇文章,描述了他是如何试图打败CAP定理的: How to beat the CAP theorem

 

作者表示不是要“击败”CAP,而是尝试对数据存储系统进行重新设计,以可控的复杂度来实现CAP。Marz认为一个分布式系统面临CAP难题的两大问题就是:在数据库中如何使用不断变化的数据,如何使用算法来更新数据库中的数据。

Marz提出了2个基本思路:

1) 数据不存在update,只存在append操作。这样就把对数据的处理由CRUD变为CR;同样的,delete操作也可以处理为add一条新记录:比如 友强取消了对山丘的关注,传统关系型数据库是在关系表删除一条记录,而Marz也可以新增一条关系为[取消]的记录。

2) 所有的数据的操作就只剩下Create和Read。把Read作为一个Query来处理,而一个Query就是一个对整个数据集执行一个函数操作。

总结,在有一定时序性,且对实时一致性要求不高的场景下可以选择使用,毕竟在问题解决域多了一把锤子。Query过程中的跨分区函数仍然是一种合并的变种(Merge)!

 

OceanBase的另类之路

既然更新数据涉及到分区问题,那么能不能把更新放到一个服务器呢[脑洞大开]?然后通过强大的软件+硬件能力一起去保障它!同时已经不修改的数据天然具备可扩展性!这是我粗暴理解OceanBase的基本设计思想。

 

link[5] 写道:作为电子商务企业,淘宝和其他公司的业务对一致性和可用性的要求高于分区容错性,数据特征是数据总量庞大且逐步增加,单位时间内的数据更新量并不大,但实时性要求很高。这就要求我们提供一套更加偏重于支持CA特性的系统,同时兼顾可分区性,并且在实时性、成本、性能等方面表现良好。

OceanBase的逻辑架构简图

 

关于UpdateServer的性能问题

其实大部分数据库每天的修改次数相当有限,只有少数修改比较频繁的数据库才有每天几亿次的修改次数。另外,数据库平均每次修改涉及的数据量很少,很多时候只有几十个字节到几百个字节。假设数据库每天更新1亿次,平均每次需要消耗100字节,每天插入1000万次,平均每次需要消耗1000字节,那么,一天的修改量为:1亿 * 100 + 1000万 *1000 = 20GB,如果内存数据结构膨胀2倍,占用内存只有40GB。而当前主流的服务器都可以配置96GB内存,一些高档的服务器甚至可以配置192GB、384GB乃至更多内存。

TiDB 

TiDB 是国内 PingCAP 团队开发的一个分布式 SQL 数据库。其灵感来自于 Google 的 F1, TiDB 支持包括传统 RDBMS 和 NoSQL 的特性。从下面这篇访谈来看,分布式事务早期版本中,他们参考的是Google 的 Percolator 的模型。目前该项目逐步纳入商用,有兴趣的朋友参考下面的link[6]。

 

[1] link:http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf

[2] link:http://www.infoq.com/cn/articles/cap-twelve-years-later-how-the-rules-have-changed/

[3] link:https://github.com/basho/riak_dt 

[4] link:http://www.cs.berkeley.edu/~brewer/cs262b/update-conflicts.pdf

[5] link:http://code.taobao.org/p/OceanBase/wiki/intro/

[6] link:http://mt.sohu.com/20160122/n435472384.shtml

 

总结:CAP是分布式领域的重要理论,但不代表完全不能有延展的解读和思考。另外[三选二]本身也是有条件成立的,不能简单误读,一切取决于应用场景。如果不加选择按照理论形式无异于刻舟求剑。

欲了解更多有关分布式缓存方面的内容,请阅读《深入分布式缓存:从原理到实践》一书。


京东购书,扫描二维码:

 

 

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
4月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
27天前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
39 5
|
3月前
|
缓存 NoSQL Java
谷粒商城笔记+踩坑(12)——缓存与分布式锁,Redisson+缓存数据一致性
缓存与分布式锁、Redisson分布式锁、缓存数据一致性【必须满足最终一致性】
152 14
谷粒商城笔记+踩坑(12)——缓存与分布式锁,Redisson+缓存数据一致性
|
2月前
|
缓存 Java 数据库
JAVA分布式CAP原则
JAVA分布式CAP原则
68 0
|
4月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
5月前
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
5月前
|
算法 前端开发
|
5月前
|
存储 缓存 NoSQL
高并发架构设计三大利器:缓存、限流和降级问题之Redis用于搭建分布式缓存集群问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之Redis用于搭建分布式缓存集群问题如何解决
104 1
|
4月前
|
存储 NoSQL 关系型数据库
(二)漫谈分布式之理论篇:用刁钻的手法掰正你那学歪的CAP与BASE理论!
大多数讲分布式的资料、课程,虽然在一开始就会先讲述CAP理论,但大家仔细想想,你在做分布式项目时,落地过这个基础理论吗?相信包括我在内,以及90%以上的开发者,没有,至于为何,本文来从不一样的角度好好唠唠CAP,以及另一个著名的BASE理论~
104 0
|
5月前
|
存储 缓存 数据库
分布式篇问题之全量缓存解决数据库和缓存的一致性问题如何解决
分布式篇问题之全量缓存解决数据库和缓存的一致性问题如何解决