java面试之分布式事务(一)

1. 事务

事务由一组操作组成的一个工作单元,工作单元应该具备原子性、一致性、隔离性和持久性

  • 原子性(Atomicity):一个事务内所有操作要么都执行,要么都不执行

  • 一致性(Consistency):数据是满足完整性约束的,也就是不会存在中间状态数据

  • 隔离性(Isolation):多个事务并发执行的时候不会互相干扰,即一个事务内部的数据对于其他事务来说是隔离的

  • 持久性(Durability):一个事务完成了之后数据就被永远保存下来,之后其他操作或故障不会对事务结果产生影响

Redis事务(不保证原子性)中某个命令失败了,之后命令还是会被处理,也不会回滚。

不支持回滚的原因:编程错误,且这种错也不可能进入到生产环境。

本地事务:用关系数据库控制事务,关系数据库通常都具有ACID特性,传统单体应用通常会将数据全部存储在一个数据库中,会借助关系数据库来完成事务控制。

事务日志

redo log(重做日志)

redo log ,用于记录 数据修改后的记录,顺序记录。假设修改 tba 表中 id=2的行数据,把Name=’B’ 修改为Name = ‘B2’ ,那么redo日志就会用来存放Name=’B2’的记录,如果这个修改在flush 到磁盘文件时出现异常,可以使用redo log实现重做操作,保证事务的持久性。

undo log(回滚日志)

undo日志用于存放数据修改被修改前的值,假设修改 tba 表中 id=2的行数据,把Name=’B’ 修改为Name = ‘B2’ ,那么undo日志就会用来存放Name=’B’的记录,如果这个修改出现异常,可以使用undo日志来实现回滚操作,保证事务的一致性。

  • 原子性:是使用 undo log来实现的,如果事务执行过程中出错或者用户执行了rollback,系统通过undo log日志返回事务开始的状态。
  • 持久性:使用 redo log来实现,只要redo log日志持久化了,当系统崩溃,即可通过redo log把数据恢复。
  • 隔离性:通过锁以及MVCC,使事务相互隔离开。
  • 一致性:通过回滚、恢复,以及并发情况下的隔离性,从而实现一致性。

2. 分布式事务

分布式系统是部署在不同结点上的系统,通过网络交互来完成协同工作。

分布式事务是在分布式系统中实现事务,是由多个本地事务组合而成。

  • 多个系统通过网络协同完成一个事务的过程(跨服务或跨服务跨数据库)

在这里插入图片描述

  • 一个系统使用多个不同数据库,一次事务操作多个数据源(跨数据库)

在这里插入图片描述

3. CAP理论

CAP理论是分布式系统设计时只能在**一致性(Consistency)、可用性(Availability)、分区容忍性(Partition Tolerance)**中满足两种。

一致性:所有节点在同一时间的数据完全一致。一致的程度不同大体可以分为强、弱、最终一致性三类。

可用性:服务一直可用,而且是正常响应时间。

分区容忍性:遇到某节点或网络分区故障(网络中断、网延延迟)时候,仍然能够对外提供满足一致性和可用性的服务。
在这里插入图片描述

验证CAP理论

在这里插入图片描述

N1节点更新了V0到V1,也想把这个消息通过M操作告诉N2节点,却发生了网络故障。

  • 系统网络发生了故障,但是系统依然可以访问,因此具有容错性
  • 牺牲一致性,响应V0给用户
  • 牺牲可用性,阻塞等待,直到网络连接恢复,数据更新操作完成再给用户响应V1

组合方式

  • CA:放弃分区容忍性,加强一致性和可用性,关系数据库按照CA进行设计,也就意味着系统不是分布式了。单体架构

  • AP:放弃一致性,加强可用性和分区容忍性,追求最终一致性,很多NoSQL数据库按照AP进行设计。(订单退款,今日退款成功,明日账户到账,12306买票)Eureka
    放弃一致性是指放弃强一致性,强一致性是写入成功立刻要查询出最新数据。追求最终一致性是指允许暂时数据不一致,只要最终在用户接受时间内数据 一致即可。

  • CP:放弃可用性,加强一致性和分区容忍性,一些强一致性要求的系统按CP进行设计,比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。zookeeper

    网络问题的存在CP系统可能会出现待等待超时,如果没有处理超时问题则整理系统会出现阻塞

4. BASE理论

BASE理论是从CAP理论发展而来的,其核心思想是即使无法做到强一致性,但每个应用都可以根据自身特点,采取适当的方式来使系统达到最终一致性。

Basically Available (基本可用) :出现故障的时候,允许损失部分可用性,即保证核心可用。

Soft state(软状态):允许系统在多个不同节点的数据副本存在数据延时。

Eventually consistent(最终一致性):系统中所有数据副本经过一定时间后,最终能够达到一致状态。

BASE理论面向的是大型高可用、可扩展分布式系统。与传统ACID特性相反,BASE提出通过牺牲强一致性来获得可用性,并允许数据段时间内的不一致,但是最终达到一致状态。同时在实际分布式场景中,不同业务对数据一致性要求不一样。因此在设计中,ACID和BASE理论往往又会结合使用。

5. 解决方案

XA协议

XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局调度者,负责各个本地资源的提交和回滚。

XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是性能不理想,无法满足高并发场景。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

2PC

二阶段提交(Two-phase commit protocol)是一种强一致性设计,引入一个事务协调者的角色来协调管理各参与者(各本地资源)的提交和回滚。

二阶段分别指的是准备(投票)和提交两个阶段。

准备阶段:协调者会给各参与者发送准备命令(除了提交事务之外)(“事务等待,可能会造成大事务”)

提交阶段:同步等待所有资源响应之后,协调者则向所有参与者发送提交或回滚事务命令。

假如在第一阶段所有参与者都返回准备成功,那么协调者则向所有参与者发送提交事务命令,然后等待所有事务都提交成功之后,返回事务执行成功。

假如在第一阶段有一个参与者返回失败,那么协调者就会向所有参与者发送回滚事务请求,即分布式事务执行失败。

在这里插入图片描述

1、应用程序通过事务协调器向两个库发起prepare,两个数据库收到消息分别执行本地事务(记录日志),但不提交,如果执行成功则回复yes,否则回复no。
2、事务协调器收到回复,只要有一方回复no则分别向参与者发起回滚事务,参与者开始回滚事务。
3、事务协调器收到回复,全部回复yes,此时向参与者发起提交事务。如果参与者有一方提交事务失败则由事务协调器发起回滚事务。
————————————————
优点:实现强一致性,部分关系数据库支持(Oracle、MySQL等);适用于数据库层面的分布式事务场景
缺点:整个事务的执行需要由协调者在多个节点之间去协调,增加了事务的执行时间;同步阻塞性能低;存在单点故障问题;不适合高并发场景

3PC

3PC(Three-phase commit protocol) 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段

其在两阶段提交基础上增加了CanCommit阶段,并引入了参与者超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。

第二阶段:PreCommit阶段

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

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

第三阶段:DoCommit阶段

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

相反,如果有一个参与者节点未完成PreCommit的反馈或者反馈超时,那么协调者都会向所有参与者节点发送abort请求,从而中断事务。

在这里插入图片描述

改进部分:引入了参与者超时机制,避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下无法释放资源的问题;增加了预提交阶段使得故障恢复之后协调者的决策复杂度降低。

TCC

2PC 和 3PC 都是数据库层面的,而 TCC(Try - Confirm - Cancel) 是业务层面的分布式事务。其核心思想是:针对每个操作都要注册一个与其对应的确认和补偿(撤销操作)。(“一次性提交,不会有事务等待造成的大事务问题”)

  • Try 检查及预留业务资源完成提交事务前的检查,并预留好资源
  • Confirm 确定执行业务操作,对try阶段预留资源正式执行
  • Cancel 取消执行业务操作,对try阶段预留资源释放

在这里插入图片描述

1、Try
下单业务由订单服务和库存服务协同完成,在try阶段订单服务和库存服务完成检查和预留资源。
订单服务检查当前是否满足提交订单的条件(比如:当前存在未完成订单的不允许提交新订单);库存服务检查当前是否有充足库存,并锁定资源。
2、Confirm
订单服务和库存服务成功完成Try后开始正式执行资源操作。
订单服务向订单写一条订单信息;库存服务减去库存。
3、Cancel
如果订单服务和库存服务有一方出现失败则全部取消操作。
订单服务需要删除新增的订单信息;库存服务将减去的库存再还原
————————————————
优点:最终保证数据的一致性,在业务层实现事务控制,灵活性好。
缺点:开发成本高,每个事务操作每个参与者都需要实现try/confirm/cancel三个接口。
注意:TCC的try/confirm/cancel接口都要实现幂等性,在为在try、confirm、cancel失败后要不断重试。

本地消息表+消息队列

本地消息表实现的是最终一致性,容忍了数据暂时不一致情况。核心思路是将分布式事务拆分成本地事务进行处理。

通过在事务主动发起方额外新建事务消息表,事务发起方处理业务和记录事务消息在本地事务中完成,轮询事务消息表的数据发送事务消息,事务被动方基于消息中间件消费事务消息表中的事务。
在这里插入图片描述

1:事务主动方处理本地事务。
事务主动方在本地事务中处理业务更新操作和写消息表操作。上面例子中库存服务阶段在本地事务中完成扣减库存和写消息表(图中 1、2)。

2:事务主动方通过消息中间件,通知事务被动方处理事务通知事务待消息。
消息中间件可以基于 Kafka、RocketMQ 消息队列,事务主动方主动写消息到消息队列,事务消费方消费并处理消息队列中的消息。上面例子中,库存服务把事务待处理消息写到消息中间件,订单服务消费消息中间件的消息,完成新增订单(图中 3 - 5)。

3:事务被动方通过消息中间件,通知事务主动方事务已处理的消息。
上面例子中,订单服务把事务已处理消息写到消息中间件,库存服务消费中间件的消息,并将事务消息的状态更新为已完成(图中 6 - 8)。为了数据的一致性,当处理错误需要重试,事务发送方和事务接收方相关业务处理需要支持幂等。
---
优点:方案轻量,容易实现;从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件特性的依赖
缺点:耦合性强;消息服务性能会受到关系型数据库并发性能的局限

可靠消息服务

基于 MQ 分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面协议基本与本地消息表一致。

在这里插入图片描述
在这里插入图片描述

1.发送方向 MQ 服务端(MQ Server)发送 half 消息。
2:MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。
3:发送方开始执行本地事务逻辑。
4:发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。
5:MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息,执行本地事务逻辑;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。
---
MQ发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。
优点:消息数据独立存储 ,降低业务系统与消息系统之间的耦合;吞吐量优于使用本地消息表方案
缺点:一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) ;业务处理服务需要实现消息状态回查接口

Saga

Saga 事务核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。

  • 每个 Saga 事务由一系列幂等的有序子事务(sub-transaction) Ti 组成。
  • 每个 Ti 都有对应的幂等补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果。

在这里插入图片描述

两种恢复策略

向前恢复(forward recovery):对应于上面第一种执行顺序,适用于必须要成功的场景,发生失败进行重试,执行顺序是类似于这样的:T1, T2, …, Tj(失败), Tj(重试),…, Tn,其中j是发生错误的子事务(sub-transaction)。该情况下不需要Ci。
在这里插入图片描述

向后恢复(backward recovery):对应于上面提到的第二种执行顺序,其中 j 是发生错误的子事务(sub-transaction),这种做法的效果是撤销掉之前所有成功的子事务,使得整个 Saga 的执行结果撤销。

在这里插入图片描述

Saga 事务两种不同实现方式

命令协调

中央协调器(Orchestrator,简称 OSO)以命令/回复的方式与每项服务进行通信,全权负责告诉每个参与者该做什么以及什么时候该做什么。

在这里插入图片描述
以电商订单的例子为例:

  • 事务发起方的主业务逻辑请求 OSO 服务开启订单事务
  • OSO 向库存服务请求扣减库存,库存服务回复处理结果。
  • OSO 向订单服务请求创建订单,订单服务回复创建结果。
  • OSO 向支付服务请求支付,支付服务回复处理结果。
  • 主业务逻辑接收并处理 OSO 事务处理结果回复。

中央协调器必须事先知道执行整个订单事务所需的流程(例如通过读取配置)。如果有任何失败,它还负责通过向每个参与者发送命令来撤销之前的操作来协调分布式的回滚。

基于中央协调器协调一切时,回滚要容易得多,因为协调器默认是执行正向流程,回滚时只要执行反向流程即可。

事件编排

没有中央协调器(没有单点风险)时,每个服务产生并观察其他服务的事件,并决定是否应采取行动。

第一个服务执行一个事务,然后发布一个事件。该事件被一个或多个服务进行监听,这些服务再执行本地事务并发布(或不发布)新的事件。

当最后一个服务执行本地事务并且不发布任何事件时,意味着分布式事务结束,或者它发布的事件没有被任何 Saga 参与者听到都意味着事务结束。

在这里插入图片描述

以电商订单的例子为例:

  • 事务发起方的主业务逻辑发布开始订单事件。
  • 库存服务监听开始订单事件,扣减库存,并发布库存已扣减事件。
  • 订单服务监听库存已扣减事件,创建订单,并发布订单已创建事件。
  • 支付服务监听订单已创建事件,进行支付,并发布订单已支付事件。
  • 主业务逻辑监听订单已支付事件并处理。

事件/编排是实现 Saga 模式的自然方式,它很简单,容易理解,不需要太多的代码来构建。如果事务涉及 2 至 4 个步骤,则可能是非常合适的。

总体比较

在这里插入图片描述

  • 2PC/3PC:依赖于数据库,能够很好的提供强一致性和强事务性,但相对来说延迟比较高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,不适合高并发和高性能要求的场景。
  • TCC:适用于执行时间确定且较短,实时性要求高,对数据一致性要求高,比如互联网金融企业最核心的三个服务:交易、支付、账务。
  • 本地消息表/MQ 事务:都适用于事务中参与方支持操作幂等,对一致性要求不高,业务上能容忍数据不一致到一个人工检查周期,事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底。
  • Saga 事务:由于 Saga 事务不能保证隔离性,需要在业务层控制并发,适合于业务场景事务并发操作同一资源较少的情况。 Saga 相比缺少预提交动作,导致补偿动作的实现比较麻烦,例如业务是发送短信,补偿动作则得再发送一次短信说明撤销,用户体验比较差。Saga 事务较适用于补偿动作容易处理的场景。

6. 幂等性

任意多次执行所产生的影响均与一次执行的影响相同。

一个方法不保证幂等,那么将无法被重试。

幂等操作实现方式

  • 操作之前在业务方法进行判断如果执行过了就不再执行
  • 缓存所有请求和处理的结果,已经处理的请求则直接返回结果
  • 在数据库表中加一个状态字段(未处理,已处理),数据操作时判断未处理时再处理。

参考

面试官问我知道的分布式事务,我一口气说了六种

Java微服务下的分布式事务介绍及其解决方案

关于分布式事务,XA协议的学习笔记(整理转载)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值