浣熊say

分布式事务面试题

时间:2024-02-26

作者:浣熊say

字数:8048 字

阅读时间:32 分钟

本站访客数:人次

  • 友情提示

  • 知识星球:知识星球专属面试小册/一对一提问交流/简历修改/职业规划建议/offer选择/模拟面试,欢迎加入 成都央国企指南 知识星球(点击链接即可查看星球的详细介绍,一定确认自己需要再加入!)
  • 浣熊say&公众号:日常更新分享央国企相关干货内容,帮助大家快速获取央国企招聘、面试信息,欢迎加入 浣熊say 公众号,免费公众号希望都大家关注下~
  • 版权声明:该网站上所有文章的版权均为作者(浣熊say)本人所有,转发、分享请注明出处,任何形式用于商业用途的请联系邮 箱:tsinghualeix@gmail.com咨询合作方案,否则必将追究法律责任!

分布式事务面试题

分布式事务概念

什么是事务?

理解什么是事务可以从两个人的银行转账说起,假设500块钱从A转到B,一般可以分为2步:

第一步:资金从A账户扣除500

第二步:资金在B账户增加500

但是,假设意外出现了,当A账户的资金扣除了500之后,程序出现了崩溃,那么A账户减少了500,但是B账户却没有增加500,这样以来资金的安全性就出现了问题。

而事务就是为了解决这类场景下的问题,事务可以保证整个转账过程中所有操作的原子性,要么所有操作同时成功,要么所有操作同时失败,这样就能够保证数据安全不出问题。

事务应该具有哪些属性?

事务实际上就是保证一段操作的原子性,要么全部都成功,要么全部都失败。它与数据库系统一样具有ACID四个特性。

  • A(Atomicity):原子性,一组操作要么全程同时成功,要么全部同时失败,不存在中间状态。
  • C(Consistency):一致性,数据库中的数据需从一个一致性状态到另外一个一致性状态
  • I(Isolation):隔离性,一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • D(Durability):持久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

什么是本地事务(Local Transaction)?

本地事务通常是指在单一数据源(数据库)的系统中执行事务操作,由于本地事务只涉及到对单个数据源的读取和写入操作。

因此,出现问题的时候可以非常方便的进行回滚来确保事务的一致性,同时,一般本地事务由数据库管理系统DBMS自己进行管理。

什么是分布式事务?

在分布式系统当中,系统服务会按照功能、模块等不同被划分到不同的物理主机上执行,同时数据库也可能存在于不同的物理主机上面。

分布式事务是在多个不同的数据源(通常是多个数据库或服务)之间执行的事务操作,这些数据源可以分布在不同的物理位置或服务器上。

这就需要确保在不同数据源之间的操作一致性,如果在事务中的某个数据源上的操作失败,必须确保其他数据源上的操作也回滚,以保持整个事务的一致性。

分布式事务通常需要使用分布式事务管理器(如 XA 协议)来协调各个数据源之间的操作,这个协调器确保所有参与者在事务成功或回滚时协同工作。

分布式事务的强一致性、弱一致性、最终一致性?

强一致性

任何一次读都能读到某个数据的最近一次写的数据。系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序一致。简单来说就是,在任意时刻,所有节点中的数据是一样的

弱一致性

数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。

最终一致性

不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。简单说,就是在一段时间后,节点间的数据会最终达到一致状态。

分布式事务基础

什么是CAP理论?

  • Consistency(一致性): 一致性要求所有数据在分布式系统中的所有节点都具有相同的数据视图。如果一个节点对数据进行了更改,那么所有其他节点应该能够立即看到这个更改。在系统执行写操作后,任何后续读操作都应该返回更新后的值。

  • Availability(可用性): 系统中的每个请求都应该得到响应,即使其中的某些节点出现故障或无法提供服务。可用性要求系统对请求作出响应,无论请求成功还是失败,而不应该出现无响应的情况。

  • Partition tolerance(分区容错性) : 系统在面临网络分区或节点之间通信失败的情况下,仍然能够继续运行。分区容忍性是指系统能够处理由于网络故障或节点故障导致的部分失去联系的情况,而不会影响整个系统的可用性。

CAP 定律表明在分布式系统中,最多只能满足 CAP 中的两个属性,而无法同时满足三个。这意味着在设计分布式系统时,必须进行权衡和选择,以满足应用程序的需求。以下是一些典型的权衡:

  • CA系统(放弃P): 指将所有数据(或者仅仅是那些与事务相关的数据)都放在一个分布式节点上,就不会存在网络分区,所以强一致性以及可用性得到满足。

  • CP系统(放弃A): 如果要求数据在各个服务器上是强一致的,然而网络分区会导致同步时间无限延长,那么如此一来可用性就得不到保障了。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性非常敏感的应用通常会做出这样的选择。

  • AP系统(放弃C): 这里所说的放弃一致性,并不是完全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求系统高可用又要求分区容错,那么就要放弃一致性了。因为一旦发生网络分区,节点之间将无法通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不一致。一些遵守BASE原则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取基本的可用性。

什么事Base理论?

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展。

  • 基本可用(Basically Available): 分布式系统在面临故障或分区的情况下仍然能够提供基本的可用性,即允许损失部分可用功能,保证核心功能可用。

  • 软状态(Soft state): 分布式系统的状态可以在时间上演化,不要求实时的一致性。这允许数据在系统中的不同部分之间存在瞬时的不一致性。即允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

  • 最终一致: 最终一致是指经过一段时间后,所有节点数据都将会达到一致。

BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。

分布式事务解决方案

2PC 方案

什么是2PC方案?

2PC(Two-Phase Commit)是一种用于实现分布式事务的协议。在分布式系统中,如果事务涉及多个参与者(即多个数据库或服务),则需要确保事务在所有参与者之间的一致性。2PC 是一种用于实现这种一致性的协议,它分为两个阶段:

  1. 准备阶段(Prepare Phase):事务的协调者(Coordinator)向所有参与者发送准备请求,并等待它们的响应。在这个阶段,每个参与者会执行事务的预提交操作,并将执行结果(即是否可以提交事务)反馈给协调者。

  2. 提交阶段(Commit Phase):如果所有参与者都成功地执行了预提交操作,则协调者向所有参与者发送提交请求。参与者收到提交请求后,会正式提交事务并释放相应资源。如果任何一个参与者在准备阶段发生了失败或者拒绝提交,那么协调者将发送回滚请求,要求所有参与者回滚事务并释放资源。

举个栗子

比如相亲2个人要去吃饭,饭店老板要求先垫付再吃饭,这时男女双方提出AA,也就是说只有男女双方都付钱才能落座吃饭,但是只要2个人中有一个不统一付款就不能落座吃饭。

  • 准备阶段: 老板要求男方付款,男方付款。老板要求女方付款,女方付款;

  • 提交阶段: 老板出餐,两人纷纷落座,如果男女双方任何一人拒绝付款,老板拒绝出餐,已付的钱原路返回;

整个事务过程由事务管理器(TM)和参与者组成,老板就是事务管理器,男女双方就是参与者,师傅管理器决策整个分布式事务在计算机中关系数据库支持的两个阶段提交协议。

2PC方案的缺点?

图 4

2PC 的优点是能够确保所有参与者在提交事务之前达成一致,从而保证了事务的原子性和一致性。然而,它也存在一些缺点,包括:

  • 阻塞风险(Blocking Risk):在准备阶段,如果有参与者长时间无法响应,或者网络出现问题,那么协调者可能会一直等待,导致整个系统阻塞。
  • 单点故障(Single Point of Failure):协调者是系统的单点,如果协调者发生故障,整个系统将无法进行分布式事务的提交。
  • 性能开销(Performance Overhead):2PC 需要多次网络通信和协调操作,这会增加系统的延迟和负载。
  • 数据不一致: 事务commit过程中,commit请求和rollback请求可能因为协调者宕机或者网络问题,导致部分参与者没有收到commit/rollback请求,而另一些参与者却收到了,这种情况下就会导致参与者之间的数据不一致。

尽管存在这些缺点,但在某些场景下,仍然可以使用 2PC 来实现分布式事务的一致性要求。随着分布式系统的发展,也出现了更多高效的分布式事务协议,如基于日志的协议(如Raft、Paxos)和基于消息的协议(如消息队列)。

TCC 方案

图 6

TCC(Try-Confirm-Cancel)是一种分布式事务解决方案,也称为补偿性事务。它通过将分布式事务拆分为三个阶段来保证最终一致性:

  1. 尝试阶段(Try):在尝试阶段,事务参与者尝试执行事务的各个操作,但不会立即提交事务。在这个阶段,事务参与者会预留必要的资源并记录相关信息,但不会对外部状态做出任何改变。

  2. 确认阶段(Confirm):如果所有事务参与者的尝试阶段都成功完成,并且没有出现异常,那么事务的协调者将发起确认请求,要求各个参与者确认提交事务。在确认阶段,事务参与者执行实际的提交操作,将预留的资源转换为正式的状态。

  3. 取消阶段(Cancel):如果任何一个事务参与者的尝试阶段失败,或者在确认阶段发生异常,那么事务的协调者将发起取消请求,要求各个参与者执行事务的回滚操作。在取消阶段,事务参与者释放预留的资源并回滚事务,恢复到事务执行前的状态。

举个简单的例子如果你用100元买了一瓶水

Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,水也是一样的。

如果有一个失败,则进行cancel(释放这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。

如果都成功,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试)。

这种方案说实话几乎很少人使用,但是也有使用的场景。因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿,会造成补偿代码巨大。

本地消息表

本地消息表的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。

图 0

对于本地消息队列来说核心是把大事务转变为小事务。还是举上面用100元去买一瓶水的例子。

1.当你扣钱的时候,你需要在你扣钱的服务器上新增加一个本地消息表,你需要把你扣钱和写入减去水的库存到本地消息表放入同一个事务(依靠数据库本地事务保证一致性。

2.这个时候有个定时任务去轮询这个本地事务表,把没有发送的消息,扔给商品库存服务器,叫他减去水的库存,到达商品服务器之后这个时候得先写入这个服务器的事务表,然后进行扣减,扣减成功后,更新事务表中的状态。

3.商品服务器通过定时任务扫描消息表或者直接通知扣钱服务器,扣钱服务器本地消息表进行状态更新。

4.针对一些异常情况,定时扫描未成功处理的消息,进行重新发送,在商品服务器接到消息之后,首先判断是否是重复的,如果已经接收,在判断是否执行,如果执行在马上又进行通知事务,如果未执行,需要重新执行需要由业务保证幂等,也就是不会多扣一瓶水。

本地消息队列是BASE理论,是最终一致模型,适用于对一致性要求不高的。实现这个模型时需要注意重试的幂等。

MQ事务

基于 MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面的协议基本与本地消息表一致。MQ事务方案整体流程和本地消息表的流程很相似,如下图:

图 1

从上图可以看出和本地消息表方案唯一不同就是将本地消息表存在了MQ内部,而不是业务数据库中。

那么MQ内部的处理尤为重要,下面主要基于 RocketMQ 4.3 之后的版本介绍 MQ 的分布式事务方案。

在本地消息表方案中,保证事务主动方发写业务表数据和写消息表数据的一致性是基于数据库事务,RocketMQ 的事务消息相对于普通 MQ提供了 2PC 的提交接口,方案如下:

图 2

正常情况:事务主动方发消息,事务主动方服务正常,没有发生故障,发消息流程如下:

发送方向 MQ 服务端(MQ Server)发送 half 消息。
MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。
发送方开始执行本地事务逻辑。
发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。
MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。

图 3

异常情况:事务主动方消息恢复,在断网或者应用重启等异常情况下,图中 4 提交的二次确认超时未到达 MQ Server,此时处理逻辑如下:

MQ Server 对该消息发起消息回查。
发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
发送方根据检查得到的本地事务的最终状态再次提交二次确认。
MQ Server基于 commit/rollback 对消息进行投递或者删除。

优点

相比本地消息表方案,MQ 事务方案优点是:

消息数据独立存储 ,降低业务系统与消息系统之间的耦合。
吞吐量大于使用本地消息表方案。

缺点

一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) 。
业务处理服务需要实现消息状态回查接口。

Saga事务

Saga是由一系列的本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或者一个事件来触发Saga中的下一个本地事务的执行。如果一个本地事务因为某些业务规则无法满足而失败,Saga会执行在这个失败的事务之前成功提交的所有事务的补偿操作。

Saga的实现有很多种方式,其中最流行的两种方式是:

基于事件的方式。这种方式没有协调中心,整个模式的工作方式就像舞蹈一样,各个舞蹈演员按照预先编排的动作和走位各自表演,最终形成一只舞蹈。处于当前Saga下的各个服务,会产生某类事件,或者监听其它服务产生的事件并决定是否需要针对监听到的事件做出响应。
基于命令的方式。这种方式的工作形式就像一只乐队,由一个指挥家(协调中心)来协调大家的工作。协调中心来告诉Saga的参与方应该执行哪一个本地事务。

最大努力通知方案

最大努力通知也称为定期校对,是对MQ事务方案的进一步优化。它在事务主动方增加了消息校对的接口,如果事务被动方没有接收到消息,此时可以调用事务主动方提供的消息校对的接口主动获取。最大努力通知的整体流程如下图:

在可靠消息事务中,事务主动方需要将消息发送出去,并且消息接收方成功接收,这种可靠性发送是由事务主动方保证的;

但是最大努力通知,事务主动方尽最大努力(重试,轮询….)将事务发送给事务接收方,但是仍然存在消息接收不到,此时需要事务被动方主动调用事务主动方的消息校对接口查询业务消息并消费,这种通知的可靠性是由事务被动方保证的。

最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。

分布式事务实现方案——Seata

Seata的核心组件

图 5

  • TC(Transaction Coordinator,事务协调器):负责事务的协调和管理,确保分布式事务的一致性,Seata部署在服务端的程序就可以理解为一个TC,用于全局跟踪分布式事务;

  • TM(Transaction Manager,事务管理器):用于管理全局事务的生命周期,包括事务的开始、提交和回滚,可以理解为Client段,用于开启一个分布式事务;

  • RM(Resource Manager,资源管理器):与各种资源(如数据库、消息队列)集成,协助进行本地事务的提交和回滚,可以理解为数据库,消息队列等资源;

Seata分布式事务的基本工作原理如下:

  • 当一个全局事务开始时,TM会生成一个全局事务ID,并将此ID传播到各个微服务。

  • 各个微服务将本地事务注册到TC上,由TC来协调和全局事务的提交或回滚。

  • 如果某个本地事务失败,TC将通知其他微服务回滚,确保全局事务的一致性。

Seata中AT模式?

Seata的AT模式本质上是一个2PC(两阶段提交)协议

  • 准备阶段(Paper Phase)

图 7

在准备阶段中,Seata会拦截“业务SQL”解析SQL语义,找到要更新的业务数据,在数据被更新前,保存下来“undo log”。

然后执行“业务SQL”更新数据,更新之后再次保存数据“redo log”。

最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。

  • 提交阶段(Commit Phase)

图 8

提交阶段负责整体的回滚和提交:

  • 如果整体事务提交成功,只需要将第一阶段保存的undo log 、redo log 和 行锁删除即可。
  • 如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否则执行全局提交,回滚用到的就是一阶段记录的“undo log”,通过回滚记录生成反向更新SQL并执行,以完成分支回滚。但是在还原数据之前首先要进行脏写校验,如果“数据库数据”和“redo log”两份数据完全一致就说明没出现脏写,可以进行业务数据还原。如果数据不一致则说明出现了脏写,需要人工处理。当然事务完成后会释放所有资源和删除所有日志。

Seata-TCC模式?

图 9

TCC也是分布式事务中的二阶段提交协议,它的全部称为Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel),具体含义如下:

  1. Try:对业务资源的检查并预留;

  2. Confirm:对业务处理进行提交,即Commit操作,只要try成功那么该步骤一定成功;

  3. Cancel:对业务处理进行取消(回滚),该步骤会对Try资源进行释放;

TCC是一种侵入性的分布式事务解决方案,以上三个操作都是业务系统自行实现,对业务系统有着非常大的侵入性,设计相对复杂。

但是TCC的优点是完全不依赖数据库,能够实现跨数据库、跨应用资源对这些不同数据访问通过侵入式编码的方式实现一个原子操作,更好的解决了在各种复杂业务场景下的分布式事务问题。

TCC和AT模式的区别?

前面我们说了,AT模式实际上就是基于2PC协议,其实现是基于支持本地ACID事务的关系型数据库:

  • 准备阶段(prepare): 在本地事务中,一并提交业务数据更新和相应回滚日志记录;

  • 提交阶段commit行为: 马上成功结束,自动异步批量清理回滚日志;

  • 提交阶段rollback行为: 通过回滚日志,自动生成补偿操作,完成数据回滚;

TCC模式不依赖于底层数据资源的事务支持,而是依赖于程序员自己手动实现的代码:

  • 一阶段prepare行为: 调用自定义的prepare逻辑;

  • 一阶段commit行为: 调用自定义的commit逻辑;

  • 二阶段rollback行为: 调用自定义的rollback逻辑;

所谓的TCC模式是指支持把自定义的分支事务纳入到全局事务管理中;

Seata-Saga模式?

什么是Saga模式?

图 10

Saga模式是Seata提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务。

当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务(执行出错的时候能够及时修复)都由业务开发实现。

Saga模式下分布式事务通常都是由事件驱动的,各个参与者之间是异步执行的,Saga模式是一种长事务解决方案。

为什么需要Saga模式?

图 11

AT、XA和TCC三种事务模式中所使用的微服务全部可以根据开发者的需求进行一定程度上的修改。

但是在一些特殊环境下,如老系统、封闭系统(无法修改,同时没有任何分布式事务的引入),那么AT、XA、TCC模式将全部不能使用。

为了解决上述问题才引入了Saga模式。如:事务参与者可能是其它公司的服务或者是遗留系统,无法改造就可以使用Saga模式。

图 12

Saga模式是Seata提供的长事务解决方案,提供了异构系统的事务统一模型

在Saga模式中,所有子业务都不在直接参与整体事务的处理(只负责本地事务的处理),而是全部交由最终调用端来负责实现。

而在进行总业务逻辑处理时,某一个子业务出现问题的时候,则自动补偿全面已经成功的其它参与者,这样一阶段的正向服务调用和二阶段的服务补偿处理全部由总业务开发实现。

什么是Saga状态机?

目前Seata提供的Saga模式只能通过状态机引擎来实现,需要开发者手动进行Saga业务流程绘制,并将其转换为Json配置文件。

而后在程序运行时,将根据子配置文件实现业务处理以及服务补偿处理,而想要进行Saga状态图绘制,一般需要通过Saga的状态机来实现。

基本原理:

  • 通过状态图来定义服务调用的流程并生成Json定义文件;

  • 状态图中一个节点可以调用一个服务,节点可以配置其它的补偿节点;

  • 状态图Json由状态机引擎驱动之行,当出现异常时状态引擎反向执行已经成功节点对应的补偿节点,讲其事务进行回滚;

  • 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能;

图 13

Seata-XA模式?

为什么要支持XA模式?

本质上来说,Seata支持的3大事务模式:AT、TCC、Saga都是补偿型的。

补偿型事务处理机制构建在事务资源之上(中间件/应用层面),事务资源本身对于分布式事务是无感知的。

事务资源对分布式事务的无感知存在一个根本性的问题:无法做到真正的全局一致性,本地事务中途可能会读取到脏数据,比如:

一条库存记录在补偿型事务处理过程当中,由100扣减为50,此时,仓库管理员链接数据库,查询统计库存就看到当前的50。

之后,事务由于意外回滚,库存会被补偿回滚为100,显然,仓库管理员查询统计到的50就是脏数据。

所以说补偿型事务是存在中间状态的,中途可能读到脏数据。

与补偿型事务不同,XA协议要求事务资源本身提供对规范和协议的支持。

因为事务资源感知并参与分布式事务处理过程,所以事务资源(数据库/消息队列)可以保证从任意视角对数据的访问有效隔离,满足全局数据一致性。

除了全局一致性这个根本价值之外,支持XA模式还有以下几个方面的好处:

  1. 业务无侵入:AT模式和XA模式都是业务无侵入的,不会给应用设计和开发带来额外负担

  2. 数据库的支持广泛:XA协议被主流数据库广泛支持,不需要额外适配即可使用;

  3. 多语言支持:因为不涉及到SQL解析,XA模式对Seata的RM的要求比较少;

  4. 易于迁移:传统的基于XA协议的应用迁移到Seata平台,使用XA模式将更加平滑;

  • 免责声明

  • 本文发布的信息,最终解释权归作者所有,如有疑问或需要进一步了解,请直接与作者联系。
  • 本文部分信息来源于网络,其版权归原作者所有,本文使用该信息仅出于分享给求职者,无任何商业用途,如有侵权,请联系作者删除。
  • 本文发布的信息旨在为应届生和社会人士提供了解相关企业招聘信息的便利,不涉及任何机密信息,且所有信息均为网络公开获取。 本人亦无任何渠道可以了解任何机密信息, 如果您认为某些内容存在侵权、泄密问题,请及时联系作者删除。
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

更多央国企信息、面试辅导、简历修改等,请关注知识星球:[成都央国企指南]