You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
17 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 23 | CAP理论这顶帽子我不想要
你好,我是聂鹏程。今天,我来继续带你打卡分布式核心技术。
在开篇词中我将分布式计算划分为了四横四纵。而在前面的文章中我们已经一起学习了四横中的分布式计算、分布式通信和分布式资源池化三横的相关知识。比如在分布式计算中我们学习了分布计算模式包括MapReduce、Stream、Actor和流计算的原理和实际应用在分布式通信中我们学习了远程调用、订阅发布和消息队列模式的原理和应用在分布式资源池化中我们学习了分布式系统架构和分布式调度架构。
相信通过对这些内容的学习,你已经对分布式技术有比较深刻的了解了。分布式系统处理的关键对象是数据,前面这些文章也都是为数据处理服务的。那么,数据本身相关的分布式技术有哪些呢?这就是接下来的几讲,我要带你学习的四横中的最后一横“分布式数据存储与管理”的相关技术。
在正式介绍分布式数据存储技术之前我需要先带你了解一个基本理论也就是CAP理论。前面提到分布式系统处理的关键对象是数据而数据其实是与用户息息相关的。CAP理论指导分布式系统的设计以保证系统的可用性、数据一致性等特征。比如电商系统中保证用户可查询商品数据、保证不同地区访问不同服务器查询的数据是一致的等。
话不多说接下来我们就一起打卡CAP理论吧。
## 什么是CAP
如果你之前没有听说过CAP理论的话看到这三个字母第一反应或许是“帽子”吧。那么在分布式领域中CAP这顶“帽子”到底是什么呢我们先来看看**这三个字母分别指的是什么**吧。
接下来我结合电商的例子带你理解CAP的含义。
假设某电商,在北京、杭州、上海三个城市建立了仓库,同时建立了对应的服务器{A, B, C}用于存储商品信息。比如某电吹风在北京仓库有20个在杭州仓库有10个在上海仓库有30个。那么CAP这三个字母在这个例子中分别代表什么呢
首先我们来看一下C。**C代表Consistency**,一致性,是指所有节点在同一时刻的数据是相同的,即更新操作执行结束并响应用户完成后,所有节点存储的数据会保持相同。
在电商系统中A、B、C中存储的该电吹风的数量应该是20+10+30=60。假设现在有一个北京用户买走一个电吹风服务器A会更新数据为60-1=59与此同时要求B和C也更新为59以保证在同一时刻无论访问A、B、C中的哪个服务器得到的数据均是59。
然后看一下A。**A代表Availability**,可用性,是指系统提供的服务一直处于可用状态,对于用户的请求可即时响应。
在电商系统中用户在任一时刻向A、B、C中的任一服务器发出请求时均可得到即时响应比如查询商品信息等。
最后我们看一下P。**P代表Partition Tolerance**,分区容错性,是指在分布式系统遇到网络分区的情况下,仍然可以响应用户的请求。网络分区是指因为网络故障导致网络不连通,不同节点分布在不同的子网络中,各个子网络内网络正常。
在电商系统中假设C与A和B的网络都不通了A和B是相通的。也就是说形成了两个分区{A, B}和{C},在这种情况下,系统仍能响应用户请求。
一致性、可用性和分区容错性,就是分布式系统的三个特征。那么,我们平时说的**CAP理论又是什么呢**
**CAP理论**指的就是在分布式系统中C、A、P这三个特征不能同时满足只能满足其中两个如下图所示。这是不是有点像分布式系统在说这顶“帽子”我不想要呢
![](https://static001.geekbang.org/resource/image/85/b0/8544ff09258cea4d6d0c6e01cc2aa2b0.png)
接下来,我就通过一个例子和你进一步解释下,**什么是CAP以及CAP为什么不能同时满足**吧。
如下图所示网络中有两台服务器Server1和Server2分别部署了数据库DB1和DB2这两台机器组成一个服务集群DB1和DB2两个数据库中的数据要保持一致共同为用户提供服务。用户User1可以向Server1发起查询数据的请求用户User2可以向服务器Server2发起查询数据的请求它们共同组成了一个分布式系统。
![](https://static001.geekbang.org/resource/image/b3/a8/b335fa77228eb37d6f3596f9419a05a8.png)
对这个系统来说分别满足C、A和P指的是
* 在满足一致性C的情况下Server1和Server2中的数据库始终保持一致即DB1和DB2内容要始终保持相同
* 在满足可用性A的情况下用户无论访问Server1还是Server2都会得到即时响应
* 在满足分区容错性P的情况下Server1和Server2之间即使出现网络故障也不会影响Server1和Server2分别处理用户的请求。
当用户发起请求时,收到请求的服务器会及时响应,并将用户更新的数据同步到另一台服务器,保证数据一致性。具体的工作流程,如下所示:
1. 用户User1向服务器Server1发起请求将数据库DB1中的数据a由1改为2
2. 系统会进行数据同步即图中的S操作将Server1中DB1的修改同步到服务器Server2中使得DB2中的数据a也被修改为2
3. 当User2向Server2发起读取数据a的请求时会得到a最新的数据值2。
![](https://static001.geekbang.org/resource/image/c7/c5/c700058c70793b7dbe721e7f15d574c5.png)
这其实是在网络环境稳定、系统无故障的情况下的工作流程。但在实际场景中,网络环境不可能百分之百不出故障,比如网络拥塞、网卡故障等,会导致网络故障或不通,从而导致节点之间无法通信,或者集群中节点被划分为多个分区,分区中的节点之间可通信,分区间不可通信。
这种由网络故障导致的集群分区情况,通常被称为“网络分区”。
在分布式系统中网络分区不可避免因此分区容错性P必须满足。接下来我们就来讨论一下**在满足分区容错性P的情况下一致性C和可用性A是否可以同时满足。**
假设Server1和Server2之间网络出现故障User1向Server1发送请求将数据库DB1中的数据a由1修改为2而Server2由于与Server1无法连接导致数据无法同步所以DB2中a依旧是1。这时**User2向Server2发送读取数据a的请求时Server2无法给用户返回最新数据那么该如何处理呢**
我们能想到的处理方式有如下两种。
**第一种处理方式是,**保证一致性C牺牲可用性AServer2选择让User2的请求阻塞一直等到网络恢复正常Server1被修改的数据同步更新到Server2之后即DB2中数据a修改成最新值2后再给用户User2响应。
![](https://static001.geekbang.org/resource/image/6e/4f/6ec73c1d7061afe932f0a850547a854f.png)
**第二种处理方式是,**保证可用性A牺牲一致性CServer2选择将旧的数据a=1返回给用户等到网络恢复再进行数据同步。
![](https://static001.geekbang.org/resource/image/51/11/518102b750f1dc43d58256eac9776611.png)
除了以上这两种方案没有其他方案可以选择。可以看出在满足分区容错性P的前提下一致性C和可用性A只能选择一个无法同时满足。
## CAP选择策略及应用
通过上面的分析你已经知道了分布式系统无法同时满足CAP这三个特性那该如何进行取舍呢
其实C、A和P没有谁优谁劣只是不同的分布式场景适合不同的策略。接下来**我就以一些具体场景为例分别与你介绍保CA弃P、保CP弃A、保AP弃C这三种策略以帮助你面对不同的分布式场景时知道如何权衡这三个特征。**
比如对于涉及钱的交易时数据的一致性至关重要因此保CP弃A应该是最佳选择。2015年发生的支付宝光纤被挖断的事件就导致支付宝就出现了不可用的情况。显然支付宝当时的处理策略就是保证了CP而牺牲了A。
而对于其他场景大多数情况下的做法是选择AP而牺牲C因为很多情况下不需要太强的一致性数据始终保持一致只要满足最终一致性即可。
最终一致性指的是,不要求集群中节点数据每时每刻保持一致,在可接受的时间内最终能达到一致就可以了。不知道你是否还记得,在[第6篇文章](https://time.geekbang.org/column/article/144970)分布式事务中介绍的基于分布式消息的最终一致性方案没错这个方案对事务的处理就是选择AP而牺牲C的例子。
这个方案中,在应用节点之间引入了消息中间件,不同节点之间通过消息中间件进行交互,比如主应用节点要执行修改数据的事务,只需要将信息推送到消息中间件,即可执行本地的事务,而不需要备应用节点同意修改数据才能真正执行本地事务,备应用节点可以从消息中间件获取数据。
### 保CA弃P
首先我们看一下保CA弃P的策略。
在分布式系统中现在的网络基础设施无法做到始终保持稳定网络分区网络不连通难以避免。牺牲分区容错性P就相当于放弃使用分布式系统。因此在分布式系统中这种策略不需要过多讨论。
既然分布式系统不能采用这种策略那单点系统毫无疑问就需要满足CA特性了。比如关系型数据库 DBMS比如MySQL、Oracle部署在单台机器上因为不存在网络通信问题所以保证CA就可以了。
### 保CP弃A
如果一个分布式场景需要很强的数据一致性或者该场景可以容忍系统长时间无响应的情况下保CP弃A这个策略就比较适合。
一个保证CP而舍弃A的分布式系统一旦发生网络分区会导致数据无法同步情况就要牺牲系统的可用性降低用户体验直到节点数据达到一致后再响应用户。
我刚刚也提到了这种策略通常用在涉及金钱交易的分布式场景下因为它任何时候都不允许出现数据不一致的情况否则就会给用户造成损失。因此这种场景下必须保证CP。
保证CP的系统有很多典型的有Redis、HBase、ZooKeeper等。接下来我就**以ZooKeeper为例带你了解它是如何保证CP的。**
首先我们看一下ZooKeeper架构图。
![](https://static001.geekbang.org/resource/image/92/2b/9284b9f7bb413bdb80032866b106d42b.png)
备注:此图引自[ZooKeeper官网](https://zookeeper.apache.org/doc/current/zookeeperOver.html)。
ZooKeeper集群包含多个节点Server这些节点会通过分布式选举算法选出一个Leader节点。在ZooKeeper中选举Leader节点采用的是ZAB算法你可以再回顾下[第4篇文章](https://time.geekbang.org/column/article/143329)中的相关内容。
在ZooKeeper集群中Leader节点之外的节点被称为Follower节点**Leader节点会专门负责处理用户的写请求**
* 当用户向节点发送写请求时如果请求的节点刚好是Leader那就直接处理该请求
* 如果请求的是Follower节点那该节点会将请求转给Leader然后Leader会先向所有的Follower发出一个Proposal等超过一半的节点同意后Leader才会提交这次写操作从而保证了数据的强一致性。
具体示意图如下所示:
![](https://static001.geekbang.org/resource/image/ef/fb/ef48459152db9be2db80dc3e9e6b92fb.png)
当出现网络分区时如果其中一个分区的节点数大于集群总节点数的一半那么这个分区可以再选出一个Leader仍然对用户提供服务但在选出Leader之前不能正常为用户提供服务如果形成的分区中没有一个分区的节点数大于集群总节点数的一半那么系统不能正常为用户提供服务必须待网络恢复后才能正常提供服务。
这种设计方式保证了分区容错性,但牺牲了一定的系统可用性。
### 保AP弃C
如果一个分布式场景需要很高的可用性,或者说在网络状况不太好的情况下,该场景允许数据暂时不一致,那这种情况下就可以牺牲一定的一致性了。
网络分区出现后,各个节点之间数据无法马上同步,为了保证高可用,分布式系统需要即刻响应用户的请求。但,此时可能某些节点还没有拿到最新数据,只能将本地旧的数据返回给用户,从而导致数据不一致的情况。
**适合保证AP放弃C的场景有很多。**比如,很多查询网站、电商系统中的商品查询等,用户体验非常重要,所以大多会保证系统的可用性,而牺牲一定的数据一致性。
以电商购物系统为例如下图所示某电吹风在北京仓库有20个在杭州仓库有10个在上海仓库有30个。初始时北京、杭州、上海分别建立的服务器{A, B, C}存储该电吹风的数量均为60个。
假如上海的网络出现了问题与北京和杭州网络均不通此时北京的用户通过北京服务器A下单购买了一个电吹风电吹风数量减少到59并且同步给了杭州服务器B。也就是说现在用户的查询请求如果是提交到服务器A和B那么查询到的数量为59。但通过上海服务器C进行查询的结果却是60。
当然待网络恢复后服务器A和B的数据会同步到CC更新数据为59最终三台服务器数据保持一致用户刷新一下查询界面或重新提交一下查询就可以得到最新的数据。而对用户来说他们并不会感知到前后数据的差异到底是因为其他用户购买导致的还是因为网络故障导致数据不同步而产生的。
![](https://static001.geekbang.org/resource/image/45/90/45a06d5dae3b859011bfc77b27bde290.png)
当然,你可能会说,为什么上海服务器不能等网络恢复后,再响应用户请求呢?可以想象一下,如果用户提交一个查询请求,需要等上几分钟、几小时才能得到反馈,那么用户早已离去了。
也就是说这种场景适合优先保证AP因为如果等到数据一致之后再给用户返回的话用户的响应太慢可能会造成严重的用户流失。
目前采用保AP弃C的系统也有很多比如CoachDB、Eureka、Cassandra、DynamoDB等。
### 对比分析
保CA弃P、保CP弃A和保AP弃C这三种策略以方便你记忆和理解。
![](https://static001.geekbang.org/resource/image/1b/45/1b961be4510d86f52eb1c4d60c40d945.jpg)
## 知识扩展CAP和ACID的“C”“A”是一样的吗
首先我们看一下CAP中的C和ACID中的C是否一致。
* CAP中的C强调的是数据的一致性也就是集群中节点之间通过复制技术保证每个节点上的数据在同一时刻是相同的。
* ACID中的C强调的是事务执行前后数据的完整性保持一致或满足完整性约束。也就是不管在什么时候不管并发事务有多少事务在分布式系统中的状态始终保持一致。具体原理可参考第6篇文章“[分布式事务All or Nothing](https://time.geekbang.org/column/article/144970)”。
其次我们看一下CAP中的A和ACID中的A。
* CAP中的A指的是可用性Availability也就是系统提供的服务一直处于可用状态即对于用户的请求可即时响应。
* ACID中的A指的是原子性Atomicity强调的是事务要么执行成功要么执行失败。
因此CAP和ACID中的“C”和“A”是不一样的不能混为一谈。
## 总结
今天我主要与你分享的是CAP理论。
首先我通过电商的例子带你了解了CAP这三个字母在分布式系统中的含义以及CAP理论并与你证明了C、A和P在分布式系统中最多只能满足两个。
然后我为你介绍了分布式系统设计时如何选择CAP策略包括保CA弃P、保CP弃A、保AP弃C以及这三种策略适用的场景。
最后,我再通过一张思维导图来归纳一下今天的核心知识点吧。
![](https://static001.geekbang.org/resource/image/79/66/7986206234a8f0477cfe0cd7b639db66.png)
相信通过今天的学习你不仅对CAP理论有了更深刻的认识并且可以针对不同场景采用哪种策略给出自己的建议。加油行动起来为你的业务场景选择一种合适的策略来指导分布式系统的设计吧。相信你一定可以的
## 思考题
CAP理论和BASE理论的区别是什么
我是聂鹏程,感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再会!