Go To My HomePage

分布式事务理论

一、分布式事务基础

事务

指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销。

事务的4个特性

事务特性

原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。


一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,数据库的完整性约束没有被破坏。


隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。


持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的

分布式事务

①、跨JVM进程产生分布式事务。典型的场景就是微服务之间通过远程调用完成事务操作。 比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减库存。

②、跨数据库实例。一个服务或者单体系统访问多个数据库实例时就会产生分布式事务。比如:用户信息和订单信息分别在两个MySQL实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务。

image-20201217141118868跨数据库实例

CAP原则

CAP原则又叫CAP定理,同时又被称作布鲁尔定理(Brewer’s theorem),指的是在一个分布式系统中,不可能同时满足以下三点

一致性(Consistency)

指强一致性,在写操作完成后开始的任何读操作都必须返回该值,或者后续写操作的结果

可用性(Availability)

可用性是指,每次向未崩溃的节点发送请求,总能保证收到响应数据(允许不是最新数据)

分区容忍性(Partition tolerance)

分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,也就是说,服务器A和B发送给对方的任何消息都是可以放弃的,也就是说A和B可能因为各种意外情况,导致无法成功进行同步,分布式系统要能容忍这种情况。除非整个网络环境都发生了故障

组合 分析
CA 满足原子和可用,放弃分区容错。即就是一个整体的应用
CP 满足原子和分区容错,即要放弃高可用。当系统被分区,为了保证原子性,必须放弃可用性,让服务停用
AP 满足可用性和分区容错,当出现分区,同时为了保证可用性,必须让节点继续对外服务,这样必然导致失去原子性
  • 舍弃P(选择C/A):单点的传统关系型数据库DBMS(MySQL/Oracle),如果采用集群就必须考虑P了
  • 舍弃A(选择C/P):分布式系统要保证P,且保证一致性,如ZooKeeper / Redis / MongoDB / HBase; .
  • 舍弃C(选择A/P):分布式系统要保证P,而且保证可用性,如CoachDB / Cassandra / DynamoDB。

Base理论

BA: Basic Available基本可用 整个系统在某些不可抗力的情况下,仍然能够保证”可用性”,即定时间内仍然能够返回一个明确的结果。”基本可用”和”可用”的区别是:

  • 一定时间可以适当延长当,例如双十一响应时间可以适当延长
  • 给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意, 返回降级页面仍然是返回明确结果。

S: Soft State柔性状态

是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统不同节点的数据副本之间进行数据同步的过程存在延时。

E: Eventual Consisstency最终一致性

同一数据的不同副本的状态,可以不需要实时一致,但要保证经过一定时间后是一致的。

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性( Eventual consistency )

二、2PC协议

2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。

在计算机中部分关系数据库如Oracle、MySQL支持两阶段提交协议:

①、准备阶段(Prepare phase):事务管理器【TM】给每个参与者【RM】发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。【Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数据文件】。如参与者执行成功,给协调者反馈同意,否则反馈中止。

2PC-Prepare

②、提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者等待超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。

2PC-commit

不管最后结果如何,第二阶段都会结束当前事务

二阶段提交有以下缺点:

  • 同步阻塞问题。执行过程中所有参与节点都是事务阻塞型的。当参与者占有公共资源时其他第三方节点访问公共资源不得不处于阻塞状
  • 单点故障。由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去。尤其在第二阶段, 协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与处于阻塞状态的问题)
  • 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调发生了故障,这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于整个分布式系统便出现了数据不致性的现象。
  • 二阶段无法解决的问题。协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

三、XA方案

​ 2PC的传统方案是在数据库层面实现的,如Oracle、MySQL都支持2PC协议,但是接口不一样。为了统一标准减少行业内不必要的对接成本,需要制定标准化的处理模型及接口标准,国际开放标准组织Open Group定义了分布式事务处理模型DTP(Distributed Transaction Processing Reference Model)。2PC的缺陷也是XA方案的缺陷。

DTP模型定义如下角色:

AP(Application Program)

即应用程序,可以理解为使用DTP分布式事务的程序

RM(Resource Manager)

即资源管理器,可以理解为事务的参与者。一般情况下是指一个数据库实例

TM(Transaction Manager)

事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理事务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务


执行流程如下:

1、应用程序(AP)分别向俩RM提出执行SQL,RM此时并未提交事务

2、TM收到RM的执行回复有失败超时,分别向其他RM发起回滚事务,回滚完毕,资源锁释放

3、TM收到RM执行回复全部成功,此时向所有RM发起提交事务,提交完毕,资源锁释放


DTP模型定义TM和RM之间通讯的接口规范叫XA,可简单理解为数据库提供的2PC接口协议,基于数据库的XA协议来实现2PC又称为XA方案。

以上三个角色之间的交互方式如下:

1、TM向AP提供 应用程序编程接口,AP通过TM提交及回滚事务。

2、TM交易中间件通过XA接口来通知RM数据库事务的开始、结束以及提交、回滚等。

四、TCC模式

​ TCC编程模式本质上也是一种二阶段协议,不同在于TCC编程模式需要与具体业务耦合。TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。TCC模式一般用于和第三方自己无法掌控的系统进行对接操作。

分支事务成功情况

分支事务失败情况

  • Try 阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm一起才能真正构成一个完整的业务逻辑。
  • Confirm阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行Confirm。通常情况下,采用TCC则认为Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。
  • Cancel阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。
  • TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了成为公用组件,是为了考虑系统结构和软件复用。TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务上下文,追踪和记录状态,由于Confirmcancel失败需进行重试,因此需要实现为幂等,幂等性是指同一个操作无论请求多少次,其结果都相同。

TCC事务要考虑允许空回滚、防悬挂控制、幂等控制

Cancel接口设计时需要允许空回滚。在Try接口因为丢包时没有收到,事务管理器会触发回滚。这时会触发Cancel接口,这时Cancel执行时发现没有对应的事务xid或主键时,需要返回回滚成功。让事务服务管理器认为己回滚,否则会不断重试,而Cancel又没有对应的业务数据可以进行回滚。


img

悬挂的意思是:Cancel比Try接口先执行,出现的原因是Try由于网络拥堵而超时,事务管理器生成回滚,触发Cancel接口,而最终又收到了Try接口调用,但是Cancel比Try先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的Try接口不应该执行,则会产生数据不一致,所以我们在Cancel空回滚返回成功之前先记录该条事务xid或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为为滚成功过,则不执行Try的业务操作。


img

幂等性的意思是:因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,需要对服务设计时进行幂等控制,通常我们可以用事务xid或业务主键判重来控制。

五、3PC协议

​ 三阶段提交又称3PC,其在两阶段提交的基础上增加了CanCommit阶段,并引入了超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。但是性能问题和不一致问题仍然没有根本解决。

第一阶段:CanCommit阶段

​ 这个阶段类似于2PC中的第二个阶段中的Ready阶段,是一种事务询问操作,事务的协调者向所有参与者询问”是否可以完成本次事务?“,如果参与者节点认为自身可以完成事务就返回”YES”,否则”NO”。而在实际的场景中参与者节点会对自身逻辑进行事务尝试,其实说白了就是检查下自身状态的健康性,看有没有能力进行事务操作。

img

第二阶段:PreCommit阶段

在阶段一中,如果所有的参与者都返回Yes的话,那么就会进入PreCommit阶段进行事务预提交。此时分布式事务协调者会向所有的参与者节点发送PreCommit请求,参与者收到后开始执行事务操作,并将Undo和Redo信息记录到事务日志中。参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈”Ack”表示我已经准备好提交了,并等待协调者的下一步指令。

否则,如果阶段一中有任何一个参与者节点返回的结果是No响应,或者协调者在等待参与者节点反馈的过程中超时(2PC中只有协调者可以超时,参与者没有超时机制)。整个分布式事务就会中断,协调者就会向所有的参与者发送“abort”请求。

第三阶段:DoCommit阶段

在阶段二中如果所有的参与者节点都可以进行PreCommit提交,那么协调者就会从预提交状态->提交状态。然后向所有的参与者节点发送“doCommit”请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈“Ack”消息,协调者收到所有参与者的Ack消息后完成事务。相反,如果有一个参与者节点未完成PreCommit的反馈或者反馈超时,那么协调者都会向所有的参与者节点发送abort请求,从而中断事务。

相比较2PC而言,3PC对于协调者(Coordinator)和参与者(Partcipant)都设置了超时时间,而2PC只有协调者才拥有超时机制。这个优化点,主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。

六、消息最终一致性

利用消息中间件的ACK机制来实现

image-20210104132630332

  • 服务A必须确保消息发送到MQ
  • 服务B消费完成必须要签收消息
  • 服务B的消息可能会被重复收到,所以服务B必须做幂等性保证

七、最大努力通知

最大努力通知也是一种解决分布式事务的方案,以下是一个是充值的例

  • 有一定的消息重复通知机制:因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知
  • 有消息校对机制:如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息 信息来满足需求

最大努力通知与可靠消息一致性区别

1)解决方案思想不同

​ 可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。

2)两者的业务应用场景不同

可靠消息一致性关注的是过程的事务一致,以异步的方式完成事务

最大努力通知关注的是结果后的通知事务,即将结果可靠的通知出去

3)技术解决方向不同

可靠消息一致性要解决消息从发出到接收的一致性,即消息发出并且被接收到

最大努力通知无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠机制是,最大努力的将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息(业务处理结果)

最大努力通知的设计方案

方案一

①、发起通知方将通知发给MQ,使用普通消息机制将通知发给MQ(不用保证消息一定到达)

②、接收通知方监听 MQ

③、接收通知方接收消息,业务处理完成回应ack

④、接收通知方若没有回应ack则MQ会重复通知(通知时间间隔会变大)

⑤、长时间没收到通知,接收通知方可通过消息校对接口来校对消息的一致性(弥补第①步)

方案二

①、发起通知方将通知发给MQ。使用可靠消息一致方案中的事务消息保证本地事务与消息的原子性,最终确保将通知先发给MQ

②、通知程序监听 MQ,接收MQ的消息

③、通知程序通过互联网接口协议(如http、webservice)调用接收通知方案接口,完成通知。通知程序调用接收通知方案接口成功就表示通知成功,即消费MQ消息成功,发起通知发将不再向通知程序投递通知消息

④、接收通知方可通过消息校对接口来校对消息的一致性

方案一和方案二的区别

  • 方案1中接收通知方与MQ接口,即接收通知方案监听 MQ,此方案主要应用与内部应用之间的通知
  • 方案2中由通知程序与MQ接口,通知程序监听MQ,收到MQ的消息后由通知程序通过互联网接口协议调用接收通知方。此方案主要应用于外部应用之间的通知,例如支付宝、微信的支付结果通知

八、汇总

分布式事务模式 介绍
AT 模式 无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景,几乎0学习成本(sql都由框架托管统一执行,会存在脏写问题)
TCC 模式 高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景(第一阶段会产生行锁,事务执行太久会锁行很久
Saga 模式 长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统(第一阶段就操作DB,会存在脏读问题)
XA模式 分布式强一致性的解决方案,但性能低而使用较少。
消息最终一致性 利用MQ的ACK机制实现,有侵入性,适合内部系统的数据流动
最大努力通知 需要消息接收方对消息处理进行保障,一般用于作为接入者对接第三方系统