本文已收录到Github仓库,里面包括计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务,设计模式、架构、校招、社招等核心知识点分享,欢迎star~Github地址:https://github.com/Tyson0314/Java-learning简介事务是应用程序中一系列严谨的操作,并且所有操作都必须成功完成,否则每次操作期间所做的所有更改都将被撤消。也就是说,事务是原子的,一个事务中的一系列操作要么成功,要么不执行。事务应该具有4个属性:原子性、一致性、隔离性和持久性。这四种特性通常被称为ACID特性。分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器和事务管理器分别位于分布式系统的不同节点上。通常分布式事务涉及对多个数据源或业务系统的操作。分布式事务也可以定义为嵌套事务,同样具有ACID事务的特点。强一致性、弱一致性、最终一致性强一致性任何读都可以读到某个数据最后写入的数据。系统中所有进程看到的操作顺序与全局时钟下的顺序一致。总之,在任何时刻,所有节点中的数据都是相同的。弱一致性数据更新后,如果容忍后续访问只能访问部分或全部数据,则为弱一致性。最终一致性并不能保证任意时刻任意节点上的同一条数据都是相同的,而是随着时间的推移,不同节点上的同一条数据总是在向收敛的方向变化。简单的说,经过一段时间后,节点之间的数据最终会达到一致的状态。由于分布式事务方案,无法实现完整的ACID保证,没有完美的解决方案可以解决所有的业务问题。因此,在实际应用中,会根据业务的不同特点,选择最适合的分布式事务方案。分布式事务的CAP基本理论一致性(consistency):数据一致更新,所有数据变化同步(强一致性)。可用性(usability):良好的响应能力。分区容错(partitionfaulttolerance):可靠性。定理:任何分布式系统只能同时满足两点,不能兼顾三点。CA系统(弃P):指将所有数据(或仅与交易相关的数据)放在一个分布式节点上,不会有网络分区。所以满足了强一致性和可用性。CP系统(弃A):如果要求数据在各个服务器上强一致,但是网络分区会导致同步时间无限延长,那么可用性就得不到保证。坚持事务性ACID(原子性、一致性、隔离性和持久性)的传统数据库和对结果的一致性非常敏感的应用程序往往会做出这种选择。AP系统(舍弃C):这里所说的舍弃一致性,并不是说完全舍弃数据一致性,而是舍弃数据的强一致性,保留数据的最终一致性。如果既要系统高可用,又要分区容错,那么就必须放弃一致性。因为一旦发生网络分区,节点之间将无法通信。为了满足高可用,每个节点只能使用本地数据提供服务,这会导致数据不一致。一些遵守BASE原则的数据库(如:Cassandra、CouchDB等)往往会放宽对一致性(足以满足最终一致性)的要求,获得一次基本可用性。BASE理论BASE是BasiclyAvailable(基本可用)、Softstate(软状态)和Eventuallyconsistent(最终一致性)三个词组的缩写。它是AP在CAP中的扩展。基本可用性:当分布式系统发生故障时,允许丢失部分可用功能,以保证核心功能可用。Softstate:允许系统存在一个不影响系统可用性的中间状态,这里指的是CAP中的不一致。最终一致性(FinalConsistency):最终一致性是指经过一段时间后,所有节点的数据都会达到一致性。BASE解决了CAP中没有网络延迟的理论,在BASE中使用软状态和最终一致性来保证延迟后的一致性。BASE和ACID是相反的。完全不同于ACID的强一致性模型。而是通过牺牲强一致性来获得可用性,允许数据在一段时间内不一致,但最终达到一致状态。分布式事务解决方案分布式事务的实现主要有以下六种方案:2PC方案TCC方案本地消息表MQ事务Saga事务尽力而为通知方案2PC方案2PC方案分为两个阶段:第一阶段:事务管理器要求每一个A涉及事务的数据库预先提交这个操作,反映是否可以提交。第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。优点:尽量保证数据的强一致性,实现成本低。所有主要数据库都有自己的实现。对于MySQL,从5.5开始就支持了。缺点:单点问题:事务管理器在整个流程中的作用非常关键。如果宕机了,比如第一阶段已经完成,第二阶段准备提交的时候事务管理器宕机了,资源管理器就会一直阻塞,导致数据库无法使用。同步阻塞:就绪后,资源管理器中的资源被阻塞,直到提交完成,资源被释放。数据不一致:虽然两阶段提交协议是为了分布式数据的强一致性而设计的,但是仍然存在数据不一致的可能性。只有部分参与者收到并执行了commit操作,其余参与者因为没有收到通知而被阻塞,此时出现数据不一致。总的来说,2PC的方案比较简单,成本也较低,但其单点问题和无法支持高并发(由于同步阻塞)仍然是其最大的弱点。TCCTCC的全称是:Try,Confirm,Cancel。Try阶段:这个阶段是指检测每个服务的资源,并锁定或预留资源。Confirm阶段:这个阶段是指在每个服务中进行实际操作。取消阶段:如果服务业务方法执行出错,那么这里需要进行补偿,即对执行成功的业务逻辑执行回滚操作。(回滚那些成功的执行)举个简单的例子,如果你用100元买了一瓶水,Try阶段:你需要检查你的钱包是否有100元并锁定这100元,水也是一样的。如果失败就取消(释放100元和这瓶水),如果取消失败不管失败重试取消,所以要保持幂等性。如果全部成功,则确认,确认扣了100元,这瓶水卖了,如果确认失败,不管是什么失败,都重试(重试会以活动日志为准)。说实话,这种方案很少有人用,但也有用到的场景。因为这个事务回滚实际上是非常依赖你自己的代码来回滚补偿的,会造成巨大的补偿代码。本地消息表的核心是通过消息日志异步执行需要分布式处理的任务。消息日志可以存储在本地文本、数据库或消息队列中,可以自动或通过业务规则手动发起重试。人工重试更多应用于支付场景,通过对账系统处理事后问题。对于本地消息队列,核心是将大事务转化为小事务。还是拿上面用100元买一瓶水为例。1.扣钱的时候,需要在扣钱的服务器上新增一个本地消息表。需要把你扣的钱和存货减水写入本地消息表到同一个事务中(根据数据库的本地事务保证一致性。2.这时候有定时任务轮询本地事务表,将未发送的消息丢给商品库存服务器,让他减去水库存。先写入本服务器的交易表,然后进行扣减,扣减成功后,更新交易表中的状态3.商品服务器通过定时任务扫描消息表或者直接通知扣费服务器,扣费服务器本地消息表进行扣费状态更新4.对于一些异常情况,定时扫描已经存在的消息没有处理成功,重新发送,商品服务器收到消息后,首先判断是否重复,如果收到,则判断是否执行。通知事务也被执行。如果没有执行,则需要重新执行。需要业务做到幂等,即不会多扣一瓶水。本地消息队列基于BASE理论,是一种最终一致性模型,适用于对一致性要求不高的。在实现该模型时,需要注意重试的幂等性。MQ事务中基于MQ的分布式事务方案,其实就是对本地消息表的封装,而本地消息表是基于MQ内部的。协议的其他方面与本地消息表基本一致。MQ事务方案的整体流程和本地消息表很相似,如下图所示:从上图可以看出,与本地消息表方案唯一不同的是本地消息table存储在MQ内部而不是业务数据库中。那么内部的MQ处理就显得尤为重要。下面主要基于RocketMQ4.3之后的版本来介绍MQ的分布式事务方案。在本地消息表方案中,保证事务主动方发送写入业务表数据和写入消息表数据的一致性是基于数据库事务。RocketMQ与普通MQ相比,事务消息提供了2PC提交接口。方案如下:正常情况:交易主动方发送消息。在这种情况下,交易主动方的服务是正常的,没有出现故障。消息发送过程如下:发送方向MQ服务器(MQServer)发送半条消息。MQServer持久化消息成功后,向发送方确认消息发送成功。发送方开始执行本地事务逻辑。发送方根据本地事务执行结果(提交或回滚)向MQServer提交二次确认。MQServer收到commit状态时会将一半消息标记为可交付,订阅者最终会收到消息;MQServer收到回滚状态后会删除半条消息,订阅者不会接受该消息。异常情况:在网络断开或者应用重启等异常情况下,事务主动方的消息恢复,图4中提交的二次确认超时后还没有到达MQServer,此时的处理逻辑为如下:MQServer为消息发起消息回传。发送方收到消息校验后,需要校验相应消息本地事务执行的最终结果。发送方根据校验得到的本地交易的最终状态重新提交二次确认。MQServer根据提交/回滚传递或删除消息。优势与本地消息表方案相比,MQ事务方案的优势在于:消息数据独立存储,降低了业务系统与消息系统之间的耦合度。吞吐量大于使用本地消息表方案。缺点发送一条消息需要两次网络请求(半条消息+提交/回滚消息)。业务处理服务需要实现消息状态查询接口。Saga事务Saga是由一系列本地事务组成的。每个本地事务更新数据库后,会发布消息或事件触发Saga中下一个本地事务的执行。如果一个本地事务因为某些业务规则不能满足而失败,Saga会对该失败事务之前所有成功提交的事务进行补偿操作。Saga的实现方式有很多种,最流行的两种方式是:基于事件的方式。这种方式没有中心协调点,整个花样就像舞蹈一样,各个舞者按照事先编好的动作和动作,各自表演,最后形成舞蹈。当前Saga下的每个服务都会产生某些类型的事件,或者监听其他服务产生的事件并决定是否响应监听到的事件。基于命令的方法。这种方法的工作形式就像一个乐队,有一个指挥(协调中心)来协调大家的工作。协调中心告诉Saga的参与者应该执行哪个本地事务。Besteffortnotificationschemebesteffortnotification也叫periodicproofreading,是对MQ事务方案的进一步优化。它在事务的活动端添加了一个用于消息整理的接口。如果交易被动方没有收到消息,可以调用交易主动方提供的整理接口主动获取。尽力而为通知的整体流程如下图所示:在可靠的消息交易中,交易主动方需要发送消息,消息接收方成功接收。这种可靠发送由交易主动方保证;但是besteffortnotification,交易主动方尽最大努力(retry,polling...)将交易发送给交易接收方,但是还是有消息收不到。此时交易被动方需要主动调用交易主动方的消息校对接口查询业务消息和消费,这种通知的可靠性由交易被动方保证。尽力而为通知适用于业务通知类型。例如,微信交易的结果通过best-effortnotification通知给每个商户,包括回调通知和交易查询接口。最后给大家分享一个Github仓库,里面有大斌编译的300多本经典计算机书籍PDF,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构还有算法,机器学习,编程生活等等,可以star一下,下次找书的时候可以直接在上面搜索,仓库持续更新中~Github地址:https://github.com/Tyson0314/java-books
