【架构师从入门到进阶】第六章服务集群优化——第六节信息一致集群CAP选择CAP定理解析P分区容错性什么是分区容错性网络八大谬误如何保证分区容错性C一致性A可用性如何取舍小结如何实现最终一致性回到信息一致节点集群本篇文章我们接着学习信息一致节点集群。我们把上一篇文章没有说完的CAP定理给大家讲一下。前面我们讲了服务整体的集群然后说了一些节点间数据同步的问题既然是多个系统间数据的同步或者说多个数据库之间数据的同步那么必然会用到CAP定理。在分布式系统中这个定理很重要。因为这个定理强调的是系统中各个节点的状态如何同步的CAP定理是这方面的基本定理也是理解分布式系统的一个起点。CAP指出在一个分布式系统中对于一致性也就是C、还有可用性A、还有P这三个特性不可能同时满足而是必须有所舍弃。我们设计分布式系统时必须在这三者之间尤其是一致性和可用性之间有所取舍和平衡。CAP这个定理是加州大学的计算机科学家提出的它的三个指标就是CAP。C一致性。A可用性。P分区容错性。这个定理它指出任何分布式系统中最多具有一致性、可用性、分区容错这三个特性中的两个也就是说这三个里面最多只能选取两个这三个特性无法全部都兼顾到必须有所取舍。举一个例子这两者里面都有一个变量叫做v初始的值都是v0。那么客户端和这两个服务都可以进行通信我们把它姑且称作a和b。首先我们来看客户端的读。可以从a读也可以从b读目前看数据没有进行变更的时候是一致的也就是初始状态的情况下是没有问题的读不会有问题。那么我们来看一下写。写操作写呢因为请求发出去之后只能到一个系统它可能到这a也可能到b。写完之后就把数据比如说从v0改成v1就是说新的值是v1。CAP定理解析P分区容错性我们先从最不好理解的这个CAP中的P开始看在系统中该如何让它发生。什么是分区容错性我们先说分区啊在一个分布式系统里面节点组成的网络本来应该是互相连通的然而有可能因为一些故障使得这些节点之间不再连通了。我们来举一个例子就拿a和b举例子。a和b假如说是两个跨区的服务器它中间有一个网络进行连接当a向b发送一条消息然后如果分区之后这边b可能无法收到。系统设计的时候必须考虑这种情况。通俗点说就是如果网络断了系统还能运行但是呢数据可能不准确或者说数据准确但系统临时不可访问。也就是网络虽然断了但是请求到达b的时候它也能读出。比如说a把v0改成v1但是v1却没有从a同步到b那么当客户端去读的时候把请求打到了b上可能读的是v0但是他还是可以用的这个时候就说他容忍了目前的这种错误所以叫做容错性。如果系统断了系统就不能用了就不具备分区容错性在分布式系统中难免会有网络不通。因为有网络的八大谬误存在大家可以了解一下八大谬误就是网络有八大谬误这个大家在学分布式系统的时候在做架构设计的时候一定得考虑网络是不可靠的。总之一点网络不可靠。八大谬误说的就是一点网络不可靠。就是网络有各种问题所以大家在考虑网络的时候都要把网络的这种不确定性给考虑进去。网络八大谬误网络八大谬误是什么呢谬误一网络总是可靠的。这句话说网络什么时候都能用不会掉链子这怎么可能呢有这么几种情况第一软件和硬件总会出错你的电脑不可能永远不停电第二人们也会修改网络中的各种配置比如说路由器的配置万一改错了就像我们自己的电脑有时候改错一个配置还上不了网呢还有就是设备停电。所以说网络总是可靠的肯定是不对的。谬误二网络没有延迟。延迟就是说等待我把一个信息从一个节点传播到另一个节点因为有距离信息传递是有速度的。有速度有距离必然就有时间。谬误三带宽是无限的。也就是说传输的数据量没有限制也不可能。带宽肯定是有限的毕竟还是收费的你都去买。如果您说你的系统中做一些文件的上传和下载比较多的话那这块肯定是必须要考虑的并且还要着重的去考虑。谬误四网络总是安全的。也不可能经常有病毒有攻击甚至我们程序的代码暴露出去总会导致我们网络的问题。谬误五网络的拓扑不会改变。网络拓扑图是指由网络节点设备和通信介质之间组成的网络结构图可以理解成部署好的节点以及节点的调用流程。就是说。我现在把网络部署成这个样子也就永远都是这个样子。这怎么可能呢随着用户量的增大或者说网络结构的升级总会变的如果不变可能就这么一个系统然后公司倒闭了它可以不变。只要你能往下一直运营它就是会变的。谬误六系统只有一个管理员。如果网络比较集中可以有一个管理员如果是用的云它可能有多个云管理员的参与。也就是说这种管理员的多个就会造成一些沟通成本也会造成信息传达的错误让网络出问题。谬误七传输代价为零。也就是说感觉网络传输什么都不费网络传输中间经过路由器有芯片的计算需要费电。谬误八网络是同构的。网络可能连接计算机和其他设备每个设备具有不同的操作系统不同的数据传输协议并且所有设备都来自于各自供应商的网络组件相连接还有网络接口有的接受json有的接受protobuf就是数据都是异构还有的是属于移动属于电信。所以说我们要保证P。因为有网络的八大缪误我们的网络总是不可靠的所以说我们要保证P就算网络有问题我们的系统也能正常用这个无法保证那么我们的分布式系统就没有意义了。如何保证分区容错性那么我们怎么保证这个P呢要保证这个P提高分区容错性的一个办法就是将数据复制到多个节点上也就是冗余。就是说在一个节点上存不行得把它放到多个节点上进行存储也就说要将数据从一个节点复制到其他的多个节点上。那么当出现分区之后这一数据就可能分布到各个分区里面了这样的话容忍性就提高了。我们举个例子做mysql集群的时候是不是得考虑P得考虑每个节点都是能用的哪怕网络断了那么我们得保证其他的是可以访问的。从上面的解释来看呢因为我们要保证P所以说数据在节点之间就有了传输就有了同步。基于数据在节点之间的传输和同步我们怎么在A和C之间做取舍呢C一致性CAP三个里面选两个要着重去挑对你有用的现在我们P确定了那么就在A和C之间进行挑选了。我们来看一致性C也就是说写操作之后的读操作必须返回新值举例来说。某条记录是v0用户向a发了一个写操作将它改成了v1。接下来用户的读操作如果说读到a那么就会从a读到一个v1。一致性要求什么呢就是我写进去什么就读出来什么这叫一致性。那读有可能从a去读也有可能去b去读如果从b读的时候b这边的值没有变化也就是说a中的值没有把v0到v1的变化传递给b那么从b这边读出来的就是v0了那么这样的话呢就不满足一致性。如果说要让a和b之间的数据一致那么就要求a在修改完数据之后向b进行同步这样的话用户从b读取数据的时候也能得到v1。一致性是指各个节点之间数据保证一致每次成功写入之后无论从哪个节点读取都能读到最新的值。相当于向所有节点的写操作是原子的你向a写了如果b没有更改那么写也是要失败的。CAP一般说的都是强一致策略强一致策略是什么呢就是写操作完之后后续的读操作都能看到最新的数据弱一致性是写操作完成后后续对数据的读取可能得到更新后的值也可能是更新前的值还有就是最终一致写操作完之后在一段时间内可能读不到但是最终经过一段时间之后都能读到最新的数据。我们现在以强一致性为例来说明刚才我们所说的这种取舍。首先我们来看不一致的情况写操作把它从a这改了b没改他去b读的时候读不到。那么强一致的情况就是说a改了a同步到bb也是把它变成了v1。大家想一下如果a在把v0改成v1这一个操作同步给b之前如果用户去b读了呢如果从b读到那么是不是读的也是不正确的那么这个时候就要规定在a把数据同步给b之前不允许客户端去b读那么是不是b在这一期间相当于一个不可用的状态。就是我要保证一致性我就牺牲了可用性。A可用性如果说保证可用性就是说我随时随地都能读b那么你就要忍受a在向b同步数据的过程中还没有同步完你就来b去读了。也就是说我要一直都能读但是有可能我读的时候还没有把数据从a同步到b所以说这个时候有可能读到的数据就是不一致的。所以说这个时候就是可用性和一致性之间必须做一个取舍就是说有他没我有我没他。如何取舍所以说我们CAP最终就集中在了在CP与AP中间选。那么如何取舍呢这是这是我们要考虑的问题。其实就是看业务的要求看我们业务是对一致性要求比较重要还是对可用性要求比较重要。比如说有一些银行的系统他对一致性的要求就比较看重比如你花了一块钱或者别人给你转了一块钱涉及到人民币的场景一致性就是最高的要求。哪怕说让你今天存钱的时候响应慢了一点也不允许让你的钱出错。所以说这个时候这种业务场景是一致性大于可用性的。还有一个就是说电商系统当中就在我们前面的文章里我们讲过了CDN。比如说上传了一个商品的图片这个商品的图片是要分发到全国各地的CDN节点当中去的那么这个时候这个图片重要吗对于用户的可用性来说你更新一个商品而导致全国好多用户看不到这个商品那么这个设计是不是就有点过分了也就是说用户可以看到商品是老的但是必但是让用户能操作能看到而并不是说你为了同步一个商品的图片不让用户来用这个系统。那么这个时候可用性就高于一致性。其实在我们的工作当中大部分情况下都是可用性大于一致性的。小结我们来小结一下。对一个分布式系统来说CAP三者中P是最基本的要求只能通过加强我们的网络建设或者设备的稳定度来提升无法通过降低A和C来提升也就是说P必须保证那么就在AP和CP中进行选择。还有一个不错的策略。就是保证可用性还有分区容错性舍弃强一致保证弱一致。大家看一下这样的话就把可用性保证了P分区容错性也保证了然后一致性我们保证了弱一致比如说有一些高并发的网站像秒杀的场景、淘宝、12306等都是都是近似的兼顾了三个特性。也就是说数据可以暂时的不一致但是迟早会一致的。比如说我们在购买东西的时候你扣了一个库存并不是说你扣了这个库存后全国马上就知道了不一定是这样的。你购买了一个商品你付了钱并不一定说这个订单真正的被你付掉有可能你付的钱还在路上因为一般的支付这种行为是通过第三方去支付的你可能通过银行的转账那么银行的转账中间就会有时差那么在这个过程当中它可能提示你订单正在支付中那么这种支付中的这个状态就是兼顾弱一致性的这个状态。因为现在还处于支付中那么过一段时间可能就支付成功了。如何实现最终一致性要实现最终一致性我们就要具备重试功能。重试功能也就是说我发出去一个东西时它没有按照我的要求去改那么我可以过一会儿再去发。或者说用一种消息队列的方式我把数据先抛出去抛到消息队列里。然后大家去订阅那个消息队列不一定马上去对下一步的数据进行更改它可以慢慢的去消费我的消息。消费消息的过程中就会涉及到消费失败如果消费失败了就需要有重试的功能。那么它还是会出现短暂的不一致的情况需要我们自己去评估是不是可以容忍这个就没有标准答案了就根据产品或者说业务场景来定。回到信息一致节点集群我们讲完CAP理论回到我们的这套集群方案当中。我们讲了这么多之后我们其实会想数据的同步太麻烦了你改这个东西得同步这么多东西还得考虑网络不可靠什么强一致、弱一致等等。考虑的东西越多整个架构出错的概率就会越大那么怎么能让我们考虑的东西少一些。其实就是减少数据的同步这就回到我们最开始所说的我们不是从读和写两个方面来说吗那么如果这些数据就没有写呢全是读呢那全是读就无所谓了只要没有写那就不存在这些问题了是吧所以说这种信息一致的集群方案适合于读多写少的场景。就是尽量让写少一些读多一些这样的话涉及到数据之间同步也就会少一些。这样的话就会较少的发生节点信息的同步并且能发挥多个信息池吞吐能力的优势。