JAVA 面试 分布式事务
1、分布式事务了解吗?你们是如何解决分布式事务问题的?
在我看来,分布式事务分为跨库事务和跨服务事务两种。
- 跨库事务:一个应用某个功能需要操作多个库实现,不同的库中存储不同的业务数据。
- 跨服务事务:一个应用某个功能需要调用多个微服务实现
事务有ACID四大特性,保证了数据的强一致性。
但是分布式系统不太一样,首先说一下CAP理论,这是分布式领域的公认定理。
- 一致性(Consistency) :更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致,不能存在中间状态。
- 可用性(Availability) :服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
- 分区容错性(Partition tolerance) :遇到任何网络分区故障时,仍然需要能够保证对外持续提供服务。
网络分区:是指分布式系统中,不同的节点分布在不同的子网络(机房/异地网络)中,由于一些特殊的原因导致这些子网络之间出现网络不连通的状态,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被切分成了若干孤立的区域。
所有的分布式系统最多只能满足其中两个属性,无法同时满足CAP三个属性,要在CAP三者中进行取舍。
对于分布式系统而言,各节点之间一定会存在网络交互,首先网络存在延迟,其次无法100%确保网络的可用,因此可以认为分区网络故障不可避免。因此如果放弃分区容错性的话,则放弃了分布式,放弃了系统的可扩展性,因此要在C和A中选择。
而CP,则表示为了数据的强制一致性,放弃可用性,当分布式事务的某个部分服务无法正常工作的话,当前功能也就不可用,一般基于 X/Open DTP 模型,通过实现 XA 接口规范,采用两阶段提交或者三阶段提交来协调事务,保证数据的强一致性。
在X/Open DTP 模型中,有AP、TM、RM三个角色。
- AP:应用程序,也就是业务层,哪些操作属于一个事务,就是AP定义的。
- TM:事务管理器,控制全局事务,管理事务声明周期,并协调各个RM。
- RM:资源管理器,可以理解为事务的参与者,一般情况下是指一个数据库的实例,通过资源管理器控制着分支事务。
在使用2PC提交时,AP接收到请求后,访问TM发起全局事务,进入阶段一提交事务阶段,向所有分支事务发送事务内容,在分支事务执行完时候操作后返回是否执行成功,TM接收到所有的分支事务都执行成功的信息后,就会进入阶段二执行事务提交阶段,给所有分支事务发送Commit请求,分支事务接收到Commit请求后,会提交分支事务,然后释放连接资源,当所有分支事务完成之后,完成全局事务。但是如果阶段一有服务返回执行失败,就会中断事务,向所有参与者发送Rollback请求。
2PC协议会存在一些问题:
- 同步阻塞:在参与者接收到 Prepare 命令后到收到 Commit/Rollback 命令之前,会锁定本地资源,阻塞时间较长,严重影响系统并发性能;
- 单点故障:整个流程严重依赖协调者,如果协调者在发送第二阶段命令前宕机,会导致参与者的事务资源一致锁定。
为了解决 2PC 的“同步阻塞”和“单点故障”问题,出了 3PC。3PC 将 2PC 的“准备阶段”一分为二,并引入了超时机制。
- 阶段一:CanCommit,协调者向所有参与者发送 CanCommit 请求。参与者评估自身状况(如资源是否充足、节点是否正常),判断是否可以进行事务操作,但并不执行事务。
- 阶段二:PreCommit,阶段一所有参与者回复 Yes,协调者就会向所有参与者发送 PreCommit 请求,参与者正式执行事务操作,但不提交。参与者执行成功后,向协调者回复 Ack。如果有参与者回复 No 或者超时,协调者向所有参与者发送 Abort(中止)请求。
- 阶段三:DoCommit,和2PC一样,但是加入了超时机制,如果参与者在 PreCommit 阶段后,长时间没有收到协调者的 DoCommit请求,它不会像 2PC 那样无限期等待和阻塞,而是会自动提交本地事务。这是因为进入 PreCommit 阶段意味着所有参与者都通过了阶段一CanCommit,从概率上讲,此时所有参与者都同意提交的可能性远大于不同意,因此默认选择提交。
虽然有了3PC,但是还是没办法保证绝对的一致性,例如阶段三说的情况,PreCommit 阶段参与者一直没有收到协调者的 DoCommit或者abort请求,可能事务需要回滚,但是默认选择提交,导致数据不一致。另外XA无法满足高并发场景、运维复杂、且不是所有资源都支持XA协议,因此在互联网中很少使用,互联网更倾向于使用AP模式,针对AP模式还提出了BASE定理。
- BA:Basically Available基本可用,指分布式系统在出现不可预知故障的时候,允许损失部分可用性—-注意,这绝不等价于系统不可用。
- 响应时间上的损失:正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
- 系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
- S:Soft state软状态,软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
- E:Eventually consistent最终一致性,强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
而BASE理论又分为通知型事务和补偿型事务。
如用户完成了一笔订单,需要增加积分,而增加积分的操作不需要立马执行,只要保证最终一致性就可以。因此可以通过MQ或者本地消息表来通知积分服务需要对用户进行的积分操作。
- MQ事务消息方案:事务发起方发送半消息到MQ,MQ通知事务发起方消息发送成功,然后事务发起方执行本地事务,根据本地事务执行结果发起Commit或者Rollback,如果是Commit,MQ将会消息发送给消息订阅方,如果是Rollback,MQ会丢弃消息;订阅方根据消息执行本地服务,执行完成将消息标记为已消费,如果多次执行失败或者其他异常,会通知运维人员人工干预。
- 本地消息表方案:在同一个事务中,对业务表进行处理的同时,对消息表新增一条数据,然后事务完成后提交本地事务,保证业务处理完成后消息一定插入消息表,然后有一个消息发送器定时扫描消息表,对需要发送的消息发送给下游服务,如果下游服务返回成功,则将消息状态设置为已发送,如果返回失败或者超时,则记录失败次数并且重试,如果一致失败,就将状态设置为失败,通知运维人员人工干预。对比MQ方案对下游服务的侵入性较低,不需要下游服务监控MQ消息。
通知型事务又可以分为异步确保型事务和最大努力通知两种。
异步确保型事务适用于内部可控的服务之间的通知,目标是解决数据一致性问题,保证多方数据最终一致,而最大努力通知适用于外部跨平台、跨企业的系统,目标是为了完成消息通知任务,尽最大努力将结果告知接收方,真没接收到也可以接受,可以存在数据不一致,后期通过对账解决数据不一致问题。
但是基于消息实现的事务并不能解决所有的业务场景,例如以下场景:某笔订单完成时,同时扣掉用户的现金。这里需要订单完成和扣现金的强制一致性,必须保证用户有足够的现金才能完成订单。
这就需要引入补偿型事务,补偿型事务的核心思想是:在分布式事务中,当一个或多个参与方的本地事务执行成功后,由于某个参与方失败而导致整个全局事务需要回滚时,系统需要主动触发并执行一系列与原业务操作相反的操作(即补偿操作),以撤销之前已成功完成的操作,使系统回到一个近似初始的状态。简单来说,就是 “做错了不要紧,再把它改回来”。这个“改回来”的动作就是补偿。
补偿型事务最经典、最系统的实现模式就是 TCC(Try-Confirm-Cancel)。
- Try:完成所有业务的检查和预留资源,为后续的正式执行做准备。它会锁定必要的资源,但不会真正提交业务。
- Confirm:在Try阶段所有参与方都成功之后,事务管理器会决定是提交还是回滚全局事务。Confirm阶段通常不会失败,因为所有检查和在Try阶段都已完成。即使因网络问题失败,也会不断重试,直到成功。
- Cancel:如果任何一个服务的Try阶段失败,则进入Cancel阶段。执行补偿操作,释放Try阶段预留的资源,回滚到事务开始前的状态。
TCC是可以解决部分场景下的分布式事务的,但是,它的一个问题在于,需要每个参与者都分别实现Try,Confirm和Cancel接口及逻辑,这对于业务的侵入性是巨大的。TCC 方案严重依赖回滚和补偿代码,最终的结果是:回滚代码逻辑复杂,业务代码很难维护。所以,TCC 方案的使用场景较少,但是也有使用的场景。比如说跟钱打交道的,支付、交易相关的场景,大家会用 TCC方案,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,保证在资金上不会出现问题。
另外还有SAGA长事务模型,可以看做一个异步的、利用队列实现的补偿事务。将长事务分为多个本地短事务,每个本地事务都有相应的执行模块和补偿模块,当Saga事务中任意一个本地事务出错时,可以通过调用相关的补偿方法恢复之前的事务,达到事务最终一致性。
我们对待分布式事务的态度就是能避免尽量避免,不能避免的话需要按情况选择合适的方法。
例如我们做的消息管理系统,就是用的通知型事务,消息管理系统在接受到发送消息后,先返回一个接受成功的信息,然后异步发送消息。对于消息发送的状态提供了两种方式让调用方获取,一种是主动通知,如果调用方提供了消息回调接口,消息管理系统会按照1s、10s、30s、1min、3min、5min的时间间隔通知调用方,直到调用方回复接受到消息;一种是被动通知,消息管理系统提供了一个接口可以查询对应消息的状态,当调用方调用并将消息最终状态返回给调用后,会移除主动通知的逻辑。
然后就是爬虫数据同步的业务,用的也是通知型事务,但是通过MQ实现的异步确保型事务,保证数据不丢失即可。一般都是要求最终一致性,而且对数据的实时性要求不高,一般都是T-1,采集到的数据写到Kafka等MQ,然后调度平台定时去消费数据,保证每天可以看到昨天采集到的数据就行
(惨兮兮,up没有这种场景)另外在XX系统,还使用了类似XA模式二阶段提交的方式,但是没有使用另外的事务管理者器TM,而是应用程序AP直接行使了TM的功能,通过redis指定key的value更新来完成二阶段提交,首先,在redis创建对应的key通过接口将key传递,然后资源管理器RM接收到调用请求,返回接受成功,然后执行本地事务,执行后将value设置为reday,然后阻塞,定时访问redis,如果value变为commit或者rollback就提交或者回滚事务。
2、了解Seata吗?介绍一下。
Seata是Spring Cloud Alibaba提供的一个开源的分布式事务解决方案,旨在提供高性能和易用的分布式事务服务。它支持AT模式、TCC模式、SAGA模式和XA模式,帮助开发者解决微服务架构中的分布式事务问题。
- AT模式:无侵入性的分布式解决方案,只需要配置好Seata并在对应的接口上添加@GlobalTransactional注解即可。
- 一阶段:TM向TC注册获取全局事务ID,然后在调用其他RM时,将全局事务ID传递过去,RM接收到请求后去TC注册分支事务,执行完之后向TC报告事务状态。在每个分支事务中,Seata拦截并解析业务SQL,找到要更新的业务数据,在数据被更新前,保存下来undo日志,然后执行业务SQL更新数据,更新之后再次保存数据redo日志,这些操作都在本地事务内完成,保证了一阶段分支事务原子性。
- 二阶段:负责整体的回滚和提交,如果一阶段中有本地事务没有通过,那么就执行全局回滚,否在执行全局提交,回滚用到的就是一阶段记录的"undo Log",通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有日志。
- TCC模式:和通用型TCC模式一样,分为Try、Confirm、Cancel,是一种侵入性的分布式解决方案,需要业务系统自己实现上边三个操作,设计相对复杂,但优点是 TCC 完全不依赖数据库,能够实现跨数据库、跨应用资源管理,对这些不同数据访问通过侵入式的编码方式实现一个原子操作,更好地解决了在各种复杂业务场景下的分布式事务问题。然后再讲一下三个操作做了什么。
- SAGA模式:SEATA提供的长事务解决方案,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
- XA模式:为了保证全局全局数据一致性。要求事务资源 本身提供对XA规范和协议的支持。
先写这些,分布式事务东西太多,up感觉掌握这些就够面试了,后边有时间再深入探究。
更多推荐


所有评论(0)