CAP
BASE理论:CAP的"碱",追求可用性
很多人可能喜欢使用事务型的分布式系统或者强一致性的分布式系统,因为方便,不需要考虑太多,就像单机系统一样。但是学了CAP理论后,你肯定知道在分布式系统中,要实现强一致性,必然会影响可用性。比如,在采用两阶段提交协议的集群系统中,要执行提交操作,需要所有节点确认和投票。所以,集群的可用性时每个节点可用性的乘积,比如,假设有一个拥有3个节点的集群,每个节点的可用性为99.9%,那么整个集群的可用性为99.7%,也就是说,每个月约宕机129.6分钟(按30天/月算),这是非常严重的问题。而解决可用性低的关键在于,根据实际场景,尽量采用可用性优先的AP模型。有人可能会觉得,这也太难了,难道没有线程的库或者方案来实现合适的AP模型?是的,的确没有。因为AP是一个动态模型,是基于业务场景特点妥协折中后设计实现的。不过我们可以借助BASE理论达成目的。在我看来,BASE理论是CAP理论中的AP的延申是对互联网大规模分布式系统的实践总结,强调可用性。几乎所有的互联网后台分布式系统都得到了BASE的支持,这个理论很重要,地位也很高。一旦掌握它,就能掌握绝大部分场景的分布式系统的架构技巧,设计出适合业务场景特点的、高可用性的分布式系统。BASE理论的核心就是基本可用(Basically Available)和最终一致性(Eventually Consistent)。也有人会提到软状态(Soft State).在我看来,软状态描述的是在实现服务可用时,系统数据的一种过度状态,也就是说不同节点间的数据副本存在短暂的不一致。那么基本可用以及最终一致性到底是什么呢?我们应该如何在实践中使用BASE理论提升系统的可用性呢?
实现基本可用的4板斧
在我看来,基本可用是指分布式系统在出现不可预知的故障时,允许损失部分功能的可用性,以保障核心功能的可用性。就像弹簧一样,遇到外界的压迫,它不是折断,而是变形伸缩,不断适应外力,实现基本的可用。
具体来说,你可以把基本可用理解为,当系统节点出现大规模故障的时候,比如专线的光纤被挖断、突发流量导致系统过载(出现了突发事件。服务被大量访问),可以通过服务降级,牺牲部分功能的可用性,以保障系统的核心功能可用。
以12306订票系统基本可用的设计为例,该订票系统在春运期间会因为开始售票后先到先得的缘故出现极其海量的请求峰值,如何解决这个问题呢?
我们可以在不同的事件出售不同区域的票,以错开访问请求,削弱请求峰值
比如,在春运期间,深圳出发的火车票在8点开售,北京出发的火车票在9点开售。这就是我们常说的流量削峰。另外,你可能已经发现了,在春运期间,自己提交的购票请求往往会在队列中等待处理,可能在几分钟或十几分钟后,才能被系统处理,然后响应处理结果,这就是我们熟悉的延迟响应。12306订票系统在出现超出系统处理能力的突然流量的情况下,会通过牺牲响应事件的可用性来保障核心功能的运行。通过流量削峰和延迟响应,系统是不是就实现了基本的可用呢?现在它不会再像最初的时候那样常常报404错误了吧?
再比如,你负责一个互联网系统,此时突然出现了网络热点事件,涌进来好多用户,产生了海量的突然流量,导致系统过载,大量图片因为网络超时无法显示。那么这个时候你可以通过哪些方法保障系统的基本可用呢?
相信你马上就想到体验降级,比如用小图片来替代原始图片,通过降低图片的清晰度和大小,来提升系统的处理能力。然后你还能想到过载保护,比如把接收到的请求放在指定的队列中排队处理,如果请求等待时间超时了(假设是100ms),则之解拒绝超时请求;如果队列满了,则清除队列中一定数量的排队请求,以保护系统不过载,实现系统的基本可用。你看,与12306的设计类似,只不过你是通过牺牲部分功能的可用性来保障互联网的核心功能运行的。说了这么多,主要是想强调:基本可用在本质上是一种妥协,即在出现节点故障或系统过载的时候,通过牺牲非核心
功能的可用性来保障核心功能的稳定运行。希望大家在后续的分布式系统的开发中,不仅能掌握流量削峰、延迟响应、体验降级、过载保护这四板斧,更能理解这4板斧背后的妥协中,从而灵活地处理不可预知的突然问题
最终一致性
在我看来,最终一致性是指系统中所有数据副本在经过一段时间的同步后最终达到一种一致的状态。也就是说,在数据一致性上,系统存在一个短暂的延迟。几乎所有的互联网系统采用的都是最终一致性,只有在确实无法使用最终一致性时,才使用强一致性或事务。比如,对于决定系统运行的敏感元数据,我们需要考虑采用强一致性;对于与钱有关的支付系统或金融系统的数据,我们需要考虑采用事务。你可以将强一致行理解为嘴仗一致性的特例,也就是说,你可以把强一致性看作不存在延迟的一致性。在实践中,你也可以这样思考:如果业务的某功能无法忍受一致性的延迟(比如分布式锁对应的数据),则可以考虑强一致性;如果业务功能能容忍短暂的一致性的延迟(比如QQ状态数据),则可以考虑最终一致性。
那么如何实现最终一致性呢?你首先要知道它以什么为准,因为这时实现最终一致性的关键。一般来说,在实际工程实践中有这样两个标准:
- 1.以最新写入的数据为准,比如,AP模型的KV存储采用的就是这种方式
- 2.以第一次写入的数据为准,如果你不希望存储的数据被更改,可以以它为准。
那实现最终一致性的具体方式是什么呢?下面介绍几种常用的方式. - 1.读时修复。在读取数据时,检测到数据的不一致并进行修复,比如Cassandra的Read Repair实现,具体来说,在向Cassandra系统查询数据的时候,如果检测到不同节点的数据副本不一致,则系统会自动修复数据
- 2.写时修复:在写入数据时,检测到数据的不一致并进行修复,比如Cassandra的Hinted Handoff实现,具体来说,在向Cassandra集群的节点之间远程写数据的时候,如果写失败就将数据缓存下来,然后定时重传,以修复数据的不一致性
- 3.异步修复,这时最常用的方式,定时对账检测副本数据的一致性,若检测到不一致则进行修复
需要注意的是,因为写时修复不需要做数据一致性对比,性能消耗比较低,对系统运行影响也不大,所以推荐在实现最终一致性时优先选择这种方式。而读时修复和异步修复需要做数据一致性对比,性能消耗比较多,所以在开发实际系统时,建议尽量优化一致性对比的算法,以降低性能消耗,避免对系统运行造成影响。
另外,再补充一点,在实现最终一致性的时候,推荐同时实现自定义写一致性级别(比如All、Quorum、One、Any),让用户可以自主选择相应的一致性级别,比如可以通过设置一致性级别为All来实现强一致性。
现在,相比你已经了解了BASE理论的核心内容了吧?不过这只是理论层面的,那么在实践中,我们该如何使用BASE理论呢?
注意
BASE理论是对CAP中一致性和可用性权衡的结果,它来源于对大规模互联网分布式系统实践的总结,是基于CAP定理逐步演化而来的。它的核心思想是,如果非必需,不推荐实现事务或强一致性,鼓励优先考虑可用性和性能,根据业务的场景特点来实现非常弹性的基本可用,以及实现数据的最终一致性。