1.事务的四大特性(面试题)
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中,一个事务由一组SQL语句组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
undo_log 解决 原子性和一致性
redo_log解决持久性
事务的隔离级别解决 隔离性
原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态,事务的中间状态不能被观察到的。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。隔离性又分为四个级别:读未提交(read uncommitted)、读已提交(read committed,解决脏读)、可重复读(repeatable read,解决虚读)、串行化(serializable,解决幻读)。
持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
2.事务实现
2.1 本地事务
@Transational
大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务(Local Transaction)。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所示:
2.2 分布式事务
随着微服务的发展,很多公司都进行了数据库垂直拆分。在这种情况下,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。(例如下单接口)
3.常见分布式事务解决方案
1.XA两段提交(低效率)-分布式事务解决方案
2.TCC三段提交(2段,高效率[不推荐(补偿代码)])
3.本地消息(MQ+Table) 最终一致性
4.事务消息(RocketMQ) (唯一不同的就是将本地消息表存在了MQ内部)
5.Seata(alibaba) (推荐)
分布式事务的五种解决方案_分布式事务解决方案_JHpku的博客-优快云博客
首先,传统的单体应用,通过 3 个 Module,在同一个数据源上更新数据来完成一项业务。很自然的,整个业务过程的数据一致性由本地事务来保证。但是在微服务中,每一个服务内部的数据一致性仍由本地事务来保证。而整个业务层面的全局数据一致性要如何保障呢?这就是微服务架构下面临的,典型的分布式事务需求:我们需要一个分布式事务的解决方案保障业务全局的数据一致性。以下便是分布式微服务中的事务解决方案
https://blog.youkuaiyun.com/m0_47671062/article/details/127935569
-
redo log, 持久性,当数据库对数据做修改的时候,需要把数据页从磁盘读到buffer pool中,然后在buffer pool中进行修改,那么这个时候buffer pool中的数据页就与磁盘上的数据页内容不一致,称buffer pool的数据页为dirty page脏数据,如果这个时候发生非正常的DB服务重启,那么这些数据还在内存,并没有同步到磁盘文件中,也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool中的data page变更结束后,把相应修改记录记录到这个文件(注意,记录日志是顺序IO),那么当DB服务发生crash的情况,恢复DB的时候,也可以根据这个文件的记录内容,重新写到到磁盘文件,这样数据就保持一致。
-
undo log,一致性,原子性。 undo日志用于存放数据被修改前的值,如果修改出现异常,可以使用undo日志来实现回滚操作,保证事务的一致性。另外InnoDB MVCC事务特性也是基于undo日志实现的。undo日志分为insert undo log(insert语句产生的日志,事务提交后直接删除)和update undo log(delete和update语句产生的日志,由于该undo log可能提供MVVC机制使用,所以不能再事务提交时删除)。
XA协议
在讲分布式事务之前,必然需要先了解XA协议。XA是一个协议,由Oracle Tuxedo [tʌkˈsiːdəʊ] 系统提出的XA分布式事务协议。
XA协议定义了分布式事务参与方的两个角色:
事务协调者(TM=Transaction Manager) 带头大哥
资源管理器/事务参与者(RM=Resource Manager) 小弟
XA协议包括两阶段提交(2PC)和三阶段提交(3PC)两种实现
2PC(二阶段提交协议)
2PC,两阶段提交,将事务的提交过程分为资源准备和资源提交两个阶段,并且由事务协调者来协调所有事务参与者,如果准备阶段所有事务参与者预提交成功,则进行第二阶段的资源提交,否则事务协调者回滚资源
1、第一阶段:预提交阶段
由事务协调者询问通知各个事务参与者,是否准备好了执行事务,具体流程图如下:
-
① 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复
-
② 各参与者执行本地事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)
-
③ 如参与者执行成功,给协调者反馈同意,否则反馈中止,表示事务不可以执行
undo一般用于事务的取消与回滚,记录的是数据修改前的值;
redo一般用于恢复已确认但未写入数据库的数据,记录的是数据修改后的值。
2、第二阶段:提交阶段
协调者收到各个参与者的准备消息后,根据反馈情况通知各个参与者commit提交或者rollback回滚
接下来分两种情况分别讨论提交阶段的过程。
(1)事务提交:
当第一阶段所有参与者都反馈同意时,协调者发起正式提交事务的请求,当所有参与者都回复同意时,则意味着完成事务,具体流程如下:
① 协调者节点向所有参与者节点发出正式提交的 commit 请求。
② 收到协调者的 commit 请求后,参与者正式执行事务提交操作,并释放在整个事务期间内占用的资源。
③ 参与者完成事务提交后,向协调者节点发送ACK消息。
④ 协调者节点收到所有参与者节点反馈的ACK消息后,完成事务。
所以,正常提交时,事务的完整流程图如下:
(2)事务回滚:
如果任意一个参与者节点在第一阶段返回的消息为中止,或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时,那么这个事务将会被回滚,具体流程如下:
① 协调者向所有参与者发出 rollback 回滚操作的请求
② 参与者利用阶段一写入的undo信息执行回滚,并释放在整个事务期间内占用的资源
③ 参与者在完成事务回滚之后,向协调者发送回滚完成的ACK消息
④ 协调者收到所有参与者反馈的ACK消息后,取消事务
所以,事务回滚时,完整流程图如下:
2PC缺点:
二阶段提交确实能够提供原子性的操作,但是不幸的是,二阶段提交还是有几个缺点的:
(1)性能问题:执行过程中,所有参与节点都是事务阻塞性的,当参与者占有公共资源时,其他第三方节点访问公共资源就不得不处于阻塞状态,为了数据的一致性而牺牲了可用性,对性能影响较大,不适合高并发高性能场景
(2)可靠性问题:2PC非常依赖协调者(带头大哥),当协调者发生故障时,尤其是第二阶段,那么所有的参与者就会都处于锁定事务资源的状态中,而无法继续完成事务操作(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
(3)数据一致性问题:在阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。
(4)二阶段无法解决的问题:协调者在发出 commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了,那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
3PC(三阶段提交协议)
3PC,三阶段提交协议,是二阶段提交协议的改进版本,三阶段提交有两个改动点:
(1)在协调者和参与者中都引入超时机制
(2)在第一阶段和第二阶段中插入一个准备阶段,保证了在最后提交阶段之前各参与节点的状态是一致的。
所以3PC会分为3个阶段,
CanCommit 准备阶段、(你能不能行?)
PreCommit 预提交阶段、
DoCommit 提交阶段,处理流程如下:
1、阶段一:CanCommit 准备阶段
协调者向参与者发送 canCommit 请求,参与者如果可以提交就返回Yes响应,否则返回No响应,具体流程如下:
(1)事务询问:协调者向所有参与者发出包含事务内容的 canCommit 请求,询问是否可以提交事务,并等待所有参与者答复。
(2)响应反馈:参与者收到 canCommit 请求后,如果认为可以执行事务操作,则反馈 yes 并进入预备状态,否则反馈 no。
2、阶段二:PreCommit 阶段
协调者根据参与者的反应情况来决定是否可以进行事务的 PreCommit 操作。根据响应情况,有以下两种可能:
(1)执行事务:
假如所有参与者均反馈 yes,协调者预执行事务,具体如下:
① 发送预提交请求:协调者向参与者发送 PreCommit 请求,并进入准备阶段
② 事务预提交 :参与者接收到 PreCommit 请求后,会执行本地事务操作,并将 undo 和 redo 信息记录到事务日志中(但不提交事务)
③ 响应反馈 :如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。
(2)中断事务:
假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断,流程如下:
① 发送中断请求 :协调者向所有参与者发送 abort 请求。
② 中断事务 :参与者收到来自协调者的 abort 请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。
3、阶段三:doCommit阶段
该阶段进行真正的事务提交,也可以分为以下两种情况:
(1)提交事务:
① 发送提交请求:协调接收到所有参与者发送的ACK响应,那么他将从预提交状态进入到提交状态,并向所有参与者发送 doCommit 请求
② 本地事务提交:参与者接收到doCommit请求之后,执行正式的事务提交,并在完成事务提交之后释放所有事务资源
③ 响应反馈:事务提交完之后,向协调者发送ack响应。
④ 完成事务:协调者接收到所有参与者的ack响应之后,完成事务。
(2)中断事务:
任何一个参与者反馈 no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务
① 发送中断请求:如果协调者处于工作状态,向所有参与者发出 abort 请求
② 事务回滚:参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
③ 反馈结果:参与者完成事务回滚之后,向协调者反馈ACK消息
④ 中断事务:协调者接收到参与者反馈的ACK消息之后,执行事务的中断。
进入doCommit阶段后,无论协调者出现问题,或者协调者与参与者之间的网络出现问题,都会导致参与者无法接收到协调者发出的 doCommit 请求或 abort 请求。此时,参与者都会在等待超时之后,继续执行事务提交。这其实基于概率来决定的,当进入第三阶段时,说明第一阶段收到所有参与者的CanCommit响应都是Yes,意味着大家都同意修改了,并且第二阶段所有的参与者对协调者的PreCommit请求也都是yes的。所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。
3PC的优缺点
与2PC相比,3PC降低了阻塞时长,并且在等待超时后,协调者或参与者会中断事务,避免了协调者单点问题,阶段三中协调者出现问题时,参与者会继续提交事务。
数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 doCommit 指令时,此时如果协调者请求中断事务,而协调者因为网络问题无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
2PC和3PC都无法保证数据绝对的一致性,一般为了预防这种问题,可以添加一个报警,比如监控到事务异常的时候,人为干预,通过脚本自动补偿差异的信息。
面试题:两阶段提交与三阶段提交的区别?
#1.增加了一个询问阶段,问了下,你能不不能行?
#2.加入了超时机制
TCC
1、什么是TCC:
TCC(Try Confirm Cancel)是应用层的两阶段提交,所以对代码的侵入性强,其核心思想是:针对每个操作,都要实现对应的确认和补偿操作,也就是业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作,第一阶段由业务代码编排来调用Try接口进行资源预留,当所有参与者的 Try 接口都成功了,事务协调者提交事务,并调用参与者的 confirm 接口真正提交业务操作,否则调用每个参与者的 cancel 接口回滚事务,并且由于 confirm 或者 cancel 有可能会重试,因此对应的部分需要支持幂等。
2、TCC的执行流程:
TCC的执行流程可以分为两个阶段,分别如下:
(1)第一阶段:Try,业务系统做检测并预留资源 (加锁,锁住资源),比如常见的下单,在try阶段,我们不是真正的减库存,而是把下单的库存给锁定住。
(2)第二阶段:根据第一阶段的结果决定是执行confirm还是cancel
Confirm:执行真正的业务(执行业务,释放锁)
Cancle:是对Try阶段预留资源的释放(出问题,释放锁)
TCC方案的优缺点
(1)TCC 事务机制相比于上面介绍的 XA 事务机制,有以下优点:
性能提升:具体业务来实现,控制资源锁的粒度变小,不会锁定整个资源。
数据最终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。
可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。
(2)缺点:TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。
Saga模式【sɑːɡə】
1.什么是Saga事务?
Saga 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Saga 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Saga 理论出自 Hector & Kenneth 1987发表的论文 Sagas。
saga模式的实现,是长事务解决方案。
Saga 事务核心思想是将长事务拆分为多个本地短事务并依次正常提交,如果所有短事务均执行成功,那么分布式事务提交;如果出现某个参与者执行本地事务失败,则由 Saga 事务协调器协调根据相反顺序调用补偿操作,回滚已提交的参与者,使分布式事务回到最初始的状态。Saga 事务基本协议如下:
(1)每个 Saga 事务由一系列幂等的有序子事务(sub-transaction) Ti 组成。
(2)每个 Ti 都有对应的幂等补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果
2、Saga的恢复策略:
对于事务异常,Saga提供了两种恢复策略,分别如下:
(1)向后恢复(backward recovery):
当执行事务失败时,补偿所有已完成的事务,是“一退到底”的方式,这种做法的效果是撤销掉之前所有成功的子事务,使得整个 Saga 的执行结果撤销。如下图:
从上图可知事务执行到了支付事务T3,但是失败了,因此事务回滚需要从C3,C2,C1依次进行回滚补偿,对应的执行顺序为:T1,T2,T3,C3,C2,C1。
(2)向前恢复(forward recovery):
对于执行不通过的事务,会尝试重试事务,这里有一个假设就是每个子事务最终都会成功,这种方式适用于必须要成功的场景,事务失败了重试,不需要补偿。流程如下图:
Saga 模式使用场景
Saga模式的优势是:
-
一阶段提交本地数据库事务,无锁,高性能;
-
参与者可以采用事务驱动异步执行,高吞吐;
-
补偿服务即正向服务的“反向”,易于理解,易于实现;
缺点:Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。
与TCC实践经验相同的是,Saga 模式中,每个事务参与者的冲正、逆向操作,需要支持:
-
空补偿:逆向操作早于正向操作时;
-
防悬挂控制:空补偿后要拒绝正向操作
-
幂等