目录数据库
什么是分布式系统?关于这点其实并无明确且统一的定义。在我看来,只要一个系统知足如下几点就能够称之为分布式系统服务器
要想更好的理解分布式系统,并正确使用甚至构建分布式系统,须要理解其中的两个关键概念——分布式系统的数据一致性和分布式系统的幂等性。网络
对于分布式系统,数据可能存在于不一样的物理节点上,节点之间只能经过网络进行通讯来协调彼此之间的状态,而网络通讯须要时间而且其自己并不十分可靠,于是如何保持数据一致性成为了分布式系统的难题。对于不一样的分布式系统,其一致性语义以及面对的一致性难题可能略有差异架构
在分布式存储系统中,为了保持系统的高可用,同时增长读操做的并发性,同一份数据会有多份副本,不一样的副本存储于不一样的节点上,以下图所示并发
在并发环境下,由于存在多个客户端同时读取同一数据在不一样节点上的副本,于是如何维护数据的一致性视图就很是重要,即对于使用该分布式系统的客户端而言,对于多副本数据的读写其表现应该和单份数据同样,一般系统是经过数据复制的方式来达到这一点的,分布式
微服务架构下,原有的单体应用按功能被拆分红一个个微服务应用,每一个微服务应用被部署在不一样的机器节点上,只完成原有单体应用的某一部分功能,操做属于该业务功能的数据库或表。彼此以前经过网络通讯的方式协调彼此之间的工做,做为总体共同对外提供服务,于是一个业务功能的实现,可能会涉及到多个微服务的调用,操做物理上不一样的多个数据库或表。好比对于下单并支付这个业务功能而言,须要调用下单微服务和支付微服务来共同完成。
函数
对于下单并支付这一业务功能,应用先调用订单微服务,在订单数据库中添加一条订单记录,成功后再调用支付微服务添加相应的支付记录,只有这两个微服务都调用成功,该业务功能才算执行成功。这个过程可能存在如下的问题:微服务
订单微服务调用成功,订单记录已落地,可是支付微服务因为各类缘由迟迟得不到响应,此时用户经过订单号查询只能查到订单记录而查不到支付记录,这对于已经成功付款的用户而言确定是没法接受的,这种状况该怎么办?大数据
订单微服务调用成功,订单记录已落地,可是支付微服务调用失败,此时订单记录和支付记录所对应的业务状态不一致,这时候系统该怎么办?.net
分布式存储系统的一致性问题,主要在于如何维持多副本的一致性视图上,即如何使多份数据对外表现的和一份数据同样。而微服务架构下的分布式应用系统,其一致性问题主要在于如何使不一样微服务的数据对同一业务状态的描述保持一致,好比对于下单并支付这一业务操做而言,下单和支付要么同时成功,要么应该同时失败,而不该该一个成功一个失败,而且在这个过程当中,某部分已经成功或失败的数据是否应该对客户端可见。在联系一下本地事务ACID中的一致性,咱们可能会产生必定的混乱:它们讲的一致性是一个东西吗?先说下个人我的理解:无论是ACID的一致性仍是不一样分布式系统中的一致性,它们本质上讲的是一件事:数据的一致性,在于正确的反应现实世界,对发生于现实世界的事情的正确描述。这就要求,一致性的数据至少要知足如下两个条件:
从这个意义上,无论是单机数据库仍是分布式存储系统仍是微服务架构下的分布式应用,对一致性的追求本质上是同样的:在知足系统自己约束的前提下,对于发生的业务操做及其执行状态的一致性描述。只不过因为分布式系统数据的分布式存储以及网络通讯情况的复杂,使得分布式系统要保持数据一致性相比单机应用要考虑更多复杂的因素,实现也要困难的多。不少文章把它们作了严格的区分,我的以为很没有必要,也不利于对于一致性的正确理解,从哲学的角度看,是割裂了事物共性和个性之间的联系。
就好像单机数据库中为事务的隔离性设置了不一样的级别,分布式系统中对数据的一致性级别也有分类。总的来讲能够分为强一致性和弱一致性两大类,弱一致性中又能够继续细分为最终一致性,因果一致性,会话一致性,单调读一致性和单调写一致性等多种,不过弱一致性中只有最终一致性比较重要,其余的能够暂时忽略。
强一致性
以带多副本的分布式存储系统为例,全部链接到分布式系统的客户端看到的某一数据的值都是同样的。当某个客户端修改了这个值,后续的全部客户端都能读取到这个更新的值,而且全部的更新操做都在这个新的值的基础上进行,直到这个值被再次修改,以下图所示,在A修改X前全部客户端都能读取到X的值为1,在A将X修改成2以后,全部客户端都能读取到这个更新后的值。
最终一致性
全部不能知足强一致性要求的都称为弱一致性,而最终一致性是其中比较强的一种。在最终一致性模型下,当数据项X被修改后,客户端并不必定能立刻看到这个更新后的值(有些可能读取到了新值,有些读取到的可能仍是旧值),可是在一段时间后,全部客户端都能读取到这个更新后的值并进行相关操做。最终一致性模型下,分布式数据最终能达到一致,可是须要通过一段时间,这段时间称为不一致窗口。
以下图所示,在A将X修改成2后,在不一致窗口内只有B能读取到X=2,其余客户端读取到的依旧是X=1。可是在不一致窗口后,全部客户端都能读取到X=2。
严格意义上来说,真正的一致性模型只有一种——强一致性,这也是一种理想化的模型。它为分布式数据维护了彻底一致的视图,使得一旦修改了数据后,全部客户端可以立刻看到这个更新后的值并基于这个新值进行后续的操做,使得咱们操做分布式数据和操做本地数据同样。在分布式系统中要实现一致性须要考虑其余因素,好比可用性和分区容忍性,而这些因素相互有制约,这种制约关系在CAP定理中被很好的进行了描述。
CAP是"Consistency","Availabilty","Partition Tolerance"的简称,分别表明了:强一致性,可用性和分区容忍性,它们的含义分别以下:
CAP定理的内容:对于一个分布式系统,没法同时实现强一致性,可用性和分区容忍性,即CAP三要素不可兼得。
因为网络的不可靠性,网络分区的状况不可避免的会发生,当出现网络分区时,不一样分区的机器没法进行通讯。分布式系统必须可以在出现网络分区的状况下继续工做,于是对于分布式系统而言,P即分区容忍性是必需要具有的要素,那么问题就转化为了,在系统知足分区容忍性的前提下,为何强一致性和可用性不可兼得。
假设数据项A的三个副本分别存储在不一样的物理节点,在某一时刻,系统状态以下图所示
当客户端将节点1上的A修改成2后,系统出现了网络分区,其中节点1和节点2在一个网络分区中,而节点3在另外一个分区中
当有客户端尝试读取节点3上的A值时,系统将面临两难困境
于是,对于知足分区容错性的系统而言,强一致性和可用性的要求难以同时被知足。其实这是很容易理解的,即便没有网络分区,由于不一样节点上的数据须要通过网络通讯来保持一致性,这个过程自己就比较花时间,当须要在给定很短的时限内基于客户端响应时,对于一致性的保证天然就比较弱。
由CAP定理可知,在分布式系统中过于追求数据的强一致性将致使可用性必定程度被牺牲,这意味着系统将不能很好的响应用户的请求,这会必定程度影响用户体验。于是对于大部分布式系统而言,应当在保证系统高可用的前提下去追求数据的一致性,BASE原则正是对这一思想的描述。
BA(Basically Available)
基本可用:系统在绝大部分时间应处于可用状态,容许出现故障损失部分可用性,但保证核心可用。
S(Soft State)
软状态:数据状态不要求在任什么时候刻都保持一致,容许存在中间状态,而该状态不影响系统可用性。对于多副本的存储系统而言,就是容许副本之间的同步存在延时,而且在这个过程当中系统依旧能够响应客户端请求。
E(Eventual Consistency)
最终一致性:尽管软状态不要求分布式数据在任什么时候刻都保持一致,但通过必定时间后,这些数据最终能达到一致性状态。
BASE理论的核心思想是:把分布式系统的可用性放在首位,放弃CAP中对数据强一致性的追求,只要系统能保证数据最终一致。
CAP描述了对于一个分布式系统而言重要的三要素:数据一致性,可用性,分区容错性之间的制约关系,当你选择了其中的两个时,就不得不对剩下的一个作必定程度的牺牲。BASE和ACID均可以看作是对CAP三要素进行取舍后的某种特殊状况
幂等的概念来自于抽象代数,好比对于一元函数来讲,知足如下条件
便可称为知足幂等性。在计算机科学中,一个操做若是屡次执行产生的影响与一次执行的影响相同,这样的操做即符合幂等性。在分布式系统中,服务消费方调用服务提供方的接口,屡次调用的结果应该与一次调用的结果同样,这正是分布式环境下幂等性的语义。为何幂等性对分布式系统而言如此重要?由于在分布式环境下,服务的调用通常采用http协议或者rpc的方式,即双方须要经过网络进行通讯,而由于网络故障或者消息超时的存在,可能服务消费方已经成功调用了服务提供方的服务接口,可是消费方并无收到来自对方的成功响应,致使消费方觉得服务调用失败从而再次进行调用,也就是说网络的不可靠性致使了服务接口被屡次调用的可能。分布式系统必须保证在这种状况下,即便接口被屡次调用,它对系统产生的影响应该与该接口只被调用一次的结果同样。
微服务架构下,处理一个业务请求可能须要调用多个微服务进行处理,之前面的下单并支付场景为例,完成该业务请求须要前后调用订单微服务的下单接口和支付微服务的支付接口,只有这两个接口都调用成功,该业务操做才算执行成功。那么微服务架构中是如何保证同属于一个业务单元的多个操做的原子性以及保证分布式数据一致性的?——答案是分布式事务。
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不一样的分布式系统的不一样节点之上
而且根据遵循的一致性原则不一样,能够分为刚性分布式事务和柔性分布式事务两大类。
这固然致使了系统可用性的下降,加上刚性事务实现时会致使同步阻塞的问题,锁定资源等问题,会极大的影响系统的吞吐量和设计弹性,因此实际上微服务架构不太会采用刚性事务。
在这个不一致窗口内,系统容许客户端对不一致的数据进行访问,于是系统的可用性相比而言会更好,加上其扩展性良好以及吞吐量的优点,通常微服务架构下都会采用柔性事务。柔性事务有多种不一样的实现方式,好比基于可靠事件的模式,基于补偿的模式,基于Sagas长事务的模式等,具体的实现原理以及优缺点对比就放到下一篇在详解解释。
在微服务架构下,不一样微服务间会有大量的基于http,rpc或者mq消息的网络通讯,接口的重复调用以及消息的重复消费可能会常常发生,好比如下这些状况
微服务架构应该具备幂等性,当接口被重复调用时,消息被重复消费时,对系统的产生的影响应该和接口被调用一次,消息被消费一次时同样。
update goods set number=number-1 where id=1
update goods set number=newNumber where id=1
总结:一般只须要对新增请求和更新请求做幂等性保证。
全局惟一ID
根据业务生成一个全局惟一ID,在调用接口时会传入该ID,接口提供方会从相应的存储系统好比Redis中去检索这个全局ID是否存在,若是存在则说明该操做已经执行过了,将拒绝本次服务请求;不然将相应该服务请求并将全局ID存入存储系统中,以后包含相同业务ID参数的请求将被拒绝。
去重表
这种方法适用于在业务中有惟一标识的插入场景。好比在支付场景中,一个订单只会支付一次,能够创建一张去重表,将订单ID做为惟一索引。把支付而且写入支付单据到去重表放入一个事务中,这样当出现重复支付时,数据库就会抛出惟一约束异常,操做就会回滚。这样保证了订单只会被支付一次。
boolean updateGoodsName(int id,String newName,int version);
数据库更新的SQL语句以下
update goods set name=#{newName},version=#{version} where id=#{id} and version<${version}
update order set status=#{status} where id=#{id} and status<#{status}
insert into goods_category (goods_id,category_id,create_time,update_time) values(#{goodsId},#{categoryId},now(),now()) on DUPLICATE KEY UPDATE update_time=now()
《大数据日知录》
《微服务设计原理与架构》
如何保证微服务接口的幂等性