笔记
https://www.it235.com/%E9%AB%98%E7%BA%A7%E6%A1%86%E6%9E%B6/SpringCloudAlibaba/seata.html
官方文档
http://seata.io/zh-cn/docs/overview/what-is-seata.html
第一章:分布式事务基础
事务
指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销。
原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。要么执行,要么不执行
一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,数据库的完整性约束没有被破坏。能量守恒,总量不变
eg: 拿转账来说,假设用户A和用户B两者的钱加起来一共是2000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是2000,这就是事务的一致性。
隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。信息彼此独立,互不干扰
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。不会轻易丢失
eg:例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
分布式事务
begin transaction;
//1.本地数据库操作:张三减少金额
//2.远程调用:让李四增加金额
commit transation;
CAP原则
一致性(Consistency)副本最新:指强一致性,在写操作完成后开始的任何读操作都必须返回该值,或者后续写操作的结果。
也就是说,在一致性系统中,一旦客户端将值写入任何一台服务器并获得响应,那么之后client从其他任何服务器读取的都是刚写入的数据一致性保证了不管向哪台服务器写入数据,其他的服务器能实时同步数据
可用性(Availability)高可用:可用性是指,每次向未崩溃的节点发送请求,总能保证收到响应数据(允许不是最新数据)
什么是分区?
在分布式系统中,不同的节点分布在不同的子网络中,由于一些特殊的原因,这些子节点之间出现了网络不通的状态,但他们的内部子网络是正常的。从而导致了整个系统的环境被切分成了若千个孤立的区域。这就是分区。
分区容忍性(Partition tolerance)
能容忍网络分区:分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供
满足一致性和可用性的服务,也就是说,服务器A和B发送给对方的任何消息都是可以放弃的,也就是说A和B可能因为各种意外情况,导致无法成功进行同步,分布式系统要能容忍这种情况。除非整个网络环境都发生了故障。
容许节点 G1/G2间传递消息的差错(延迟或丢失),而不影响系统继续运行。以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据—致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
分布式系统中,必须满足 CAP 中的 P,此时只能在 C/A 之间作出取舍
一个还不错的策略是:保证可用性和分区容错,舍弃强一致性,但保证最终一致性,比如一些高并发的站点(秒杀、淘宝、12306)。最终近似于兼顾了三个特性。
一致性
数据一致性
一致性可以分为强一致性与弱一致性。所谓强一致性,即复制是同步的,弱一致性,即复制是异步的
CAP回顾
CAP理论告诉我们一个悲惨但不得不接受的事实——我们只能在C、A、P中选择两个条件。而对于业务系统而言,我们往往选择牺牲一致性来换取系统的可用性和分区容错性。不过这里要指出的是,所谓的"牺牲一致性"并不是完全放弃数据—致性,而是牺牲强—致性换取弱—致性。
强—致性
系统中的某个数据被成功更新后,后续任何对该数据的读取操作都将得到更新后的值;
也称为:原子一致性(Atomic Consistency)线性一致性(Linearizable Consistency)
两个要求:
·任何—次读都能读到某个数据的最近—次写的数据。
·系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序—致。
简言之,在任意时刻,所有节点中的数据是一样的。例如,对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强—致性。
总结:
·一个集群需要对外部提供强一致性,所以只要集群内部某一台服务器的数据发生了改变,那么就需要等待集群内其他服务器的数据同步完成后,才能正常的对外提供服务。
·保证了强—致性,务必会损耗可用性。
弱一致性
系统中的某个数据被更新后,后续对该数据的读取操作可能得到更新后的值,也可能是更改前的值。
但即使过了不—致时间窗口这段时间后,后续对该数据的读取也不一定是最新值
所以说,可以理解为数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。
最终—致性
是弱一致性的特殊形式,存储系统保证在没有新的更新的条件下,最终所有的访问都是最后更新的值。
不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。
简单说,就是在一段时间后,节点间的数据会最终达到一致状态。
一致性总结
弱一致性即使过了不一致时间窗口,后续的读取也不一定能保证一致,而最终一致过了不一致窗口后,后续的读取一定一致
Base理论
· BA: Basic Available_基本可用
。整个系统在某些不可抗力的情况下,仍然能够保证"可用性",即一定时间内仍然能够返回一个明确的结果。只不过"基本可用“和"高可用”的区别是:
“—定时间“可以适当延长当举行大促时,响应时间可以适当延长
给部分用户返回一个降级页面给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意,返回降级页面仍然是返回明确结果。
. S : Soft State柔性状态:是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统不同节点的数据副本之间进行数据同步的过程存在延时。
. E: Eventual Consisstency.最终一致性︰同—数据的不同副本的状态,可以不需要实时一致,但一定要保证经过一定
时间后仍然是—致的。
BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency )。
分布式事务协议
二阶段提交2PC
二阶段提交协议(Two-phase Commit,即 2PC)是常用的分布式事务解决方案,即将事务的提交过程分为两个阶段来进行处理
阶段
准备阶段
提交阶段
参与角色
协调者:事务的发起者
参与者:事务的执行者
1.第—阶段(voting phase_投票阶段)o :
1.协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复
2.各参与者执行事务操作,将undo和redo 信息记入事务日志中(但不提交事务)
3.如参与者执行成功,给协调者反馈同意,否则反馈中止
2.第二阶段(commit phase提交执行阶段) :
当协调者节点从所有参与者节点获得的相应消息都为同意时:
1.协调者节点向所有参与者节点发出正式提交( commit )的请求。
2.参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
3.参与者节点向协调者节点发送ack完成消息。
4.协调者节点收到所有参与者节点反馈的ack完成消息后,完成事务。
如果任一参与者节点在第一阶段返回的响应消息为中止,或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:
1.协调者节点向所有参与者节点发出回滚操作(rollback )的请求。
2.参与者节点利用阶段1写入的undo信息执行回滚,并释放在整个事务期间内占用的资源。3.参与者节点向协调者节点发送ack回滚完成消息。
4.协调者节点受到所有参与者节点反馈的ack回滚完成消息后,取消事务。
不管最后结果如何,第二阶段都会结束当前事务。
二阶段提交看起来确实能够提供原子性的操作,但是不幸的是,二阶段提交还是有几个缺点的:
1.性能问题:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
2可靠性问题:参与者发生故障。协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。协调者发生故障。参与者会一直阻塞下去。需要额外的备机进行容错。
3.数据—致性问题:二阶段无法解决的问题:协调者在发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
优点
尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)
缺点
实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。
三阶段提交(3PC)
三阶段提交协议,是二阶段提交协议的改进版本,三阶段提交有两个改动点。
在协调者和参与者中都引入超时机制。
在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有canCommit 、PreCommit . DoCommit三个阶段。处理流程如下:
1.阶段—:CanCommit阶段
3PC的CanCommit 阶段其实和2PC的准备阶段很像。协调者向参与者发送commit 请求,参与者如果可以提交就返回Yes响应,否则返回No响应。
1.事务询问协调者向所有参与者发出包含事务内容的cancommit请求,询问是否可以提交事务,并等待所有参与者答复。
⒉响应反馈参与者收到cancommit请求后,如果认为可以执行事务操作,则反馈yes并进入预备状态,否则反馈no。
2.PreCommit阶段
协调者根据参与者的反应情况来决定是否可以进行事务的 Precommit 操作。根据响应情况,有以下两种可能。
假如所有参与者均反馈yes,协调者预执行事务。
1.发送预提交请求︰协调者向参与者发送PreCommit请求,并进入准备阶段
⒉.事务预提交∶参与者接收到 Precommit 请求后,会执行事务操作,并将undo和redo信息记录到事务日志中
(但不提交事务)
3.响应反馈︰如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。
假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。
1.发送中断请求︰协调者向所有参与者发送abort请求。
⒉.中断事务︰参与者收到来自协调者的 abort 请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。
3.doCommit阶段该阶段进行真正的事务提交,也可以分为以下两种情况。
注意:进入阶段3后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的doCommit请求或abort 请求。此时,参与者都会在等待超时之后,继续执行事务提交。
3.1执行提交
所有参与者均反馈ack 响应,执行真正的事务提交
1.发送提交请求协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。
3…事务提交参与者接收到docommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
3.响应反馈事务提交完之后,向协调者发送ack响应。
4.完成事务协调者接收到所有参与者的ack响应之后,完成事务。
3.2中断事务
任何一个参与者反馈no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务
1.发送中断请求如果协调者处于工作状态,向所有参与者发出abort请求
⒉.事务回滚参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
3.反馈结果参与者完成事务回滚之后,向协调者反馈ACK消息
4.中断事务协调者接收到参与者反馈的ACK消息之后,执行事务的中断。
注意
在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者abort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响应,但是他有理由相信:成功提交的几率很大。)
优点:相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。
缺点:数据不一致问题依然存在,当在参与者收到precommit请求后等待docommit 指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
分布式事务解决方案
几种方式:
TCC
全局消息
基于可靠消息服务的分布式事务
最大努力通知
事务补偿(TCC)
TCC方案是一种应用层面侵入业务的两阶段提交。是目前最火的一种柔性事务方案,其核心思想是∶针对每个操作,都要注册—个与其对应的确认和补偿撤销)操作
1.第—阶段
Try(尝试)∶主要是对业务系统做检测及资源预留(加锁,锁住资源)
⒉.第二阶段
本阶段根据第一阶段的结果,决定是执行confirm还是cancel
Confirm(确认):执行真正的业务执行业务,释放锁
Cancle(取消)︰是预留瓷源的取酒出问题,释放钻
案例
为了方便理解,下面以电商下单为例进行方案解析,这里把整个过程简单分为扣减库存,订单创建2个步骤,库存服务和订单服务分别在不同的服务器节点上。
假设商品库存为100,购买数量为2,这里检查和更新库存的同时,冻结用户购买数量的库存,同时创建订单,订单状态为待确认。
1Try阶段
TCC机制中的 Try 仅是一个初步操作,它和后续的确认一起才能真正构成一个完整的业务逻辑,这个阶段主要完成:
·完成所有业务检查(—致性)。
·预留必须业务资源( 准隔离性)。.Try尝试执行业务。
Confirm / Cancel阶段
根据Try阶段服务是否全部正常执行,继续执行确认操作(Confirm)或取消操作(Cancel)。
Confirm和Cancel操作满足幂等性,如果Confirm或Cancel 操作执行失败,将会不断重试直到执行完成。Confirm:当Try阶段服务全部正常执行,执行确认业务逻辑操作
这里使用的资源一定是Try阶段预留的业务资源。在TCC事务机制中认为,如果在Try阶段能正常的预留资源,那Confirm 一定能完整正确的提交。
Confirm阶段也可以看成是对Try阶段的一个补充,Try+Confirm一起组成了一个完整的业务逻辑。Cancel:当Try阶段存在服务执行失败,进入Cancel阶段
Cancel取消执行,释放Try 阶段预留的业务资源,上面的例子中,Cancel操作会把冻结的库存释放,并更新订单状态为取消。
最终—致性保证
.TCC事务机制以初步操作(Try)为中心的,确认操作(Confirm)和取消操作(Cancel)都是围绕初步操作(Ty)而展开。因此,Try阶段中的操作,其保障性是最好的,即使失败,仍然有取消操作(Cancel)可以将其执行结果撤销。
·Try阶段执行成功并开始执行Confirm 阶段时,默认Confirm阶段是不会出错的。也就是说只要Try成功,
Confirm—定成功TCC设计之初的定义。
Confirm与Cancel如果失败,由TCC框架进行重试补偿·存在极低概率在CC环节彻底失败,则需要定时任务或人工介入
方案总结
TCC事务机制相对于传统事务机制(X/Open XA),TCC事务机制相比于上面介绍的XA事务机制,有以下优点:
·性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。
数据最终一致性:基于Confirm和Cancel的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。·可靠性:解决了XA协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。
缺点:TCC的 Try、Confirm和Cancel操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。
本地消息表
方案简介
核心思路是将分布式事务拆分成本地事务进行处理。
方案通过在事务主动发起方额外新建事务消息表,事务发起方处理业务和记录事务消息在本地事务中完成,轮询事务消息表的数据发送事务消息,事务被动方基于消息中间件消费事务消息表中的事务。
这样设计可以避免"业务处理成功+事务消息发送失败",或"业务处理失败+事务消息发送成功"的棘手情况出现,保证2个系统事务的数据一致性。
处理流程
下面把分布式事务最先开始处理的事务方称为事务主动方,在事务主动方之后处理的业务内的其他事务称为事务被动方。
为了方便理解,下面继续以电商下单为例进行方案解析,这里把整个过程简单分为扣减库存,订单创建⒉个步骤。
库存服务和订单服务分别在不同的服务器节点上,其中库存服务是事务主动方,订单服务是事务被动方。
事务的主动方需要额外新建事务消息表,用于记录分布式事务的消息的发生、处理状态。
整个业务处理流程如下:
方案总结
方案的优点如下:
·从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对MQ中间件特性的依赖方案轻量,容易实现。
缺点如下:
与具体的业务场景绑定,耦合性强,不可公用。
消息数据与业务数据同库,占用业务系统资源。
业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限。
MQ事务方案
方案简介
基于MQ的分布式事务方案其实是对本地消息表的封装,将本地消息表基于MQ内部,其他方面的协议基本与本地消息表—致。
处理流程
下面主要基于RocketMQ 4.3之后的版本介绍MQ的分布式事务方案。
在本地消息表方案中,保证事务主动方发写业务表数据和写消息表数据的一致性是基于数据库事务,RocketMQ的事务消息相对于普通MQ,相对于提供了2PC的提交接口,方案如下:
正常情况:事务主动方发消息
这种情况下,事务主动方服务正常,没有发生故障,发消息流程如下:·图中1:发送方向MQ服务端(MQ Server)发送half 消息。
·图中2:MQ Server将消息持久化成功之后,向发送方ack确认消息已经发送成功。·图中3:发送方开始执行本地事务逻辑。
·图中4:发送方根据本地事务执行结果向MQ Server提交二次确认(commit或是 rollback)。
图中5:MQ Server收到commit状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到rollback状态则删除半消息,订阅方将不会接受该消息。
异常情况:事务主动方消息恢复
在断网或者应用重启等异常情况下,图中4提交的二次确认超时未到达MQ Server,此时处理逻辑如下:
·图中5:MQ Server对该消息发起消息回查。
·图中6:发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。·图中7︰发送方根据检查得到的本地事务的最终状态再次提交二次确认。
·图中8: MQ Server基于commit/rollback 对消息进行投递或者删除。
介绍完RocketMQ的事务消息方案后,由于前面已经介绍过本地消息表方案,这里就简单介绍RocketMQ分布式事务:
事务主动方基于MQ通信通知事务被动方处理事务,事务被动方基于MQ返回处理结果。
如果事务被动方消费消息异常,需要不断重试,业务处理逻辑需要保证幂等。
如果是事务被动方业务上的处理失败,可以通过MQ通知事务主动方进行补偿或者事务回滚。
方案总结
相比本地消息表方案,MQ事务方案优点是:
·消息数据独立存储,降低业务系统与消息系统之间的耦合。
·吞吐量由于使用本地消息表方案。
缺点是:
.一次消息发送需要两次网络请求(half消息+ commit/rollback消息)。业务处理服务需要实现消息状态回查接口。
Saga 事务:最终一致性
Saga 事务核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。
处理流程
Saga事务基本协议如下:
·每个Saga事务由—系列幂等的有序子事务(sub-transaction)Ti组成。·每个Ti都有对应的幂等补偿动作Ci,补偿动作用于撤销Ti造成的结果。
可以看到,和TCC相比,Saga没有"预留"动作,它的Ti就是直接提交到库。
介绍完分布式事务相关理论和常见解决方案后,最终的目的在实际项目中运用,因此,总结一下各个方案的常见的使用场景∶
. 2PC/3PC:依赖于数据库,能够很好的提供强—致性和强事务性,但相对来说延迟比较高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,不适合高并发和高性能要求的场景。
TCC:适用于执行时间确定且较短,实时性要求高,对数据一致性要求高,比如互联网金融企业最核心的三个服务:交易、支付、账务。
本地消息表/MQ事务︰都适用于事务中参与方支持操作幂等,对一致性要求不高,业务上能容忍数据不一致到一个人工检查周期,事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底。
Saga事务:由于Saga事务不能保证隔离性,需要在业务层控制并发,适合于业务场景事务并发操作同一资源较少的情况。Saga相比缺少预提交动作,导致补偿动作的实现比较麻烦,例如业务是发送短信,补偿动作则得再发送一次短信说明撤销,用户体验比较差。Saga事务较适用于补偿动作容易处理的场景。
总结
ACID
原子性、—致性、隔离性、持久性
CAP
·一致性:数据在多个副本直接保持一致性,一个副本的更新成功,其他副本也必须更新成功,此特性要求客户端可获
取到最新的数据
可用性:系统在合理时间内返回
·分区容忍性:在分布式网络遇到分区故障时,依然可以提供满足一致性和可用性的服务
三者不可兼得。并不是说整个系统都只能选择一种CP or AP,根据不同的业务特定选择
BASE
基本可用、软状态、最终—致性,采用合适的方式最终—致
分布式事务协议
. 2PC
。 优点:实现简单、原理简单
。 缺点:数据不—致,脑裂、单点问题、同步阻塞.
3PC
。优点:减小了参与者阻塞的范围;出现单点问题后,仍然可以提交。 缺点:数据不—致
分布式事务解决方案
1.TCC
2.本地消息表
3.MQ事务
4.saga
第二章:Seata基础
Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造—站式的分布式解决方案。
·对业务无侵入:即减少技术架构上的微服务化所带来的分布式事务问题对业务的侵入
·高性能:减少分布式事务解决方案所带来的性能消耗
官方站点:
源码: https://github.com/seata/seata
文档: http://seata.io/zh-cn/index.html
Seata术语
TC(Transaction Coordinator) 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) 事务管理器·
定义全局事务的范用:开始全局事务、提交或回滚全局事务。
RM(Resource Manager) 资源管理者
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并
驱动分支事务提交或回滚。
UNDO_LOG表:回滚日志
- UNDo_LOG 必须在每个业务数据库中创建,用于保存回滚操作数据
2.当全局提交时,UNDo_LOG记录直接删除
3.当全局回滚时,将现有数据撤销,还原至操作前的状态,详见beforelmage和afterlmage
第三章:AT模式详解
AT模式运行机制
AT模式的特点就是对业务无入侵式,整体机制分二阶段提交
·两阶段提交协议的演变:
。一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
。二阶段:
·提交异步化,非常快速地完成。
回滚通过—阶段的回滚日志进行反向补偿。
在AT模式下,用户只需关注自己的业务SQL,用户的业务sQL作为一阶段,Seata框架会自动生成事务的二阶段提交和回滚操作。
Seata具体实现步骤
1.TM端使用@GlobalTransaction进行全局事务开启、提交、回滚
2.TM开始RPC调用远程服务
3.RM端seata-client通过扩展DataSourceProxy,实现自动生成 UNDo_Log 与Tc上报
4.TM告知TC提交/回滚全局事务
5.TC通知RM各自执行commit/rollback操作,同时清除undo_log
搭建Seata TC协调者
1.http://seata.io/zh-cn/blog/download.html下载最新版本,并解压缩
修改conf/file.conf文件
将mode="file"改为mode=“db” 表示直接走数据库
2.db部分配置mysql相关信息
db {
DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = “druid”
dbType = “mysql”
//注意如果是mysql8版本,此处采用cj驱动,url后缀需要加serverTimezone=Asia/Shanghai
driverClassName = “com.mysql.cj.jdbc.Driver”
url = “jdbc:mysql://rm-bp17dq6iz79761b8fxo.mysql.rds.aliyuncs.com:3306/seata?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8”
//用户名和密码
user = “seata_test”
password = “- seata1234abcd!”
minConn = 5
maxConn = 30
globalTable = “global_table”
branchTable = “branch_table”
lockTable = “lock_table”
queryLimit = 100
maxWait = 5000
}
数据库对应上述配置新建seata的DB,并分配给指定的用户权限
初始化table,注意此处一定要初始化,并且1.0之后的版本没有sql脚本,这里我贴出来
3.修改registry.conf文件
这个文件内容分为 registry和config两部分,为了方便,我们使用之前学过的nacos注册中心,你也可以采用默认的 file方式
将registry和config 部分的由 type="file”都换成type="nacos”(注意是2处地方)。
将2处的nacos配置根据实际情况需要调整参数,本机启动nacos则不需要调整
nacos {
application = “seata-server”
serverAddr = “nacos.it235.com:80”
group = “SEATA_GROUP”
namespace = “343f2aa2-1a42-43ea-b078-33ab7d58bd6a”
cluster = “default”
username = “nacos”
password = “nacos”
}
4.修改conf/logback.xml 的文件(可选操作)
将${user.home}改为具体的seata目录,我这里是 F: \hliedu\cloud\seata-server-1.3.0\seata,那么配置如下
5.双击启动bin/seata-server.bat文件启动服务(有可能出现闪退)
6.为了方便查看更加清晰的日志,建议使用cmd的方式启动
打开CMD窗口,使用cd命令切换到seata的bin目录o输入seata-server.bat回车
7.启动成功的结果如下
使用Nacos作为Seata的配置中心
思考
如果不用Nacos,那seata的默认配置中心在哪?
我们编辑registry.conf文件,找到config部分,内容如下:
config {
file、nacos 、apollo、zk、consul、etcd3
type = “file”
nacos {
…
}
consul {
…
}
apollo {
…
}
zk {
…
}
etcd3 {
…
}
– 重点
file {
name = “file.conf”
}
}
没错,就是 file.conf文件,接下来我们不再依靠该文件而是nacos,要让seata使用nacos作为配置中心,我们需要按照以下步骤进行操作。
1.保证你的 Nacos服务已经启动,且可正常连接,
⒉添加命名空间seata,获取到 namespace 的id
3.修改registry.conf文件中的registry部分type = “nacos”,同时修改nacos部分的配置
nacos {
application = “seata-server”
serverAddr = “nacos.it235.com:80”
group = “SEATA_GROUP”
namespace = “f46bbdaa-f11e-414f-9530-e6a18cbf91f6”
cluster = “default”
username = “nacos”
password = “nacos”
}
4.修改registry.conf文件中的config部分type = “nacos”,同时修改nacos部分的配置
nacos {
serverAddr = “nacos.it235.com:80”
namespace = “f46bbdaa-f11e-414f-9530-e6a18cbf91f6”
group = “SEATA_GROUP”
username = “nacos”
password = “nacos”
}
5.下载nacos-config.脚本和config.txt
地址https://github.com/seata/seata/tree/develop/script/config-center
nacos-config.sh2和nacos-config.py选择一个:在seata目录下新建script目录,将nacos-config.sh放入script目录下
config.txt:该文件存放在将seata目录下,与conf、lib目录同级,seata的非常全的配置内容,可通过nacos-config.sh 脚本推送到nacos配置中心
修改 config.txt 的内容,下述1处暂时无需修改,2处需要换成你的DB连接路径
打开git bash或linux类命令行,执行sh脚本(注意脚本是否有执行的权限)
启动seata-server.bat,并查看nacos服务,若seata服务注册成功,表示注册中心和配置中心成功
重点关注属性
server端 client端
registry.type registry.type
config.type config.type
#store.mode=db需要以下配置 service.vgroupMapping.my_test_tx_group
store.db.driverClassName service.default.grouplist
store.db.url service.disableGlobalTransaction
store.db.user
store.db.password
#store.mode=redis 需要以下配置
store.redis.host
store.redis.port
store.redis.database
store.redis.password
常用修改三处
Registry地址
Config地址
vgroupMapping值,server和client对应上,可以都用一个,如果每个服务不同,则server中配置好所有的
service.grouplist.default用来指定TC地址,如果有注册中心,则无需指定,只在registry type为file时候起作用
编写AT模式代码
RM实现
1.创建订单和库存服务的DB
2.各数据库加入undo_log表
3.添加seatapom.xml依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
io.seata
seata-spring-boot-starter
io.seata
seata-spring-boot-starter
1.3.0
4.yml配置
seata:
enabled: true
application-id: ${spring.application.name}
事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
tx-service-group: ${spring.application.name}-tx-group
config:
type: nacos
# 需要和server在同一个注册中心下
nacos:
namespace: f46bbdaa-f11e-414f-9530-e6a18cbf91f6
serverAddr: nacos.it235.com:80
# 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
username: “nacos”
password: “nacos”
registry:
type: nacos
nacos:
# 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
application: seata-server
server-addr: nacos.it235.com:80
group: SEATA_GROUP
namespace: f46bbdaa-f11e-414f-9530-e6a18cbf91f6
username: “nacos”
password: “nacos”
5.编写业务代码
6.浏览器访问模拟添加订单请求,http://localhost:6770/order/create,查看数据库,此时单个服务搭建完成
7.依葫芦画瓢,搭建库存服务,同时保证库存服务正常启动注册
8.注意nacos中需要存在对应的service.vgroupMapping
TM实现
搭建business服务,pom、bootstrap.yml与RM基本一致,并提供FeignClient调用组件
FeignClient组件代码编写
调用层代码编写
@GlobalTransactional, 分布式事务的发起者的方法上加上此注解,即可开启AT模式
异常汇总
没有可用服务No available service,该问题是没有连接到seata-server造成,内容如下
seata-server的配置列表中有一项比较重要的配置,该配置需要手动添加;
service.vgroupMapping.orderServiceGroup=default,其中orderServiceGroup是你手动指定的名称(自定义),同时bootstrap.yml中的seata.tx-service-group 的值要为orderServiceGroup ,如没找到则会报错
2.连接TC超时,Timeout的情况
AT模式原理解析
TC相关的表解析
global_table : 主同争劳每当有一个全局事务发起后,就会在该表中记录全局事务的ID
branch_table : 分支事务记录每一个分支事务的ID,分支事务操作的哪个数据库等信息
lock_table :全局锁
日志分析
1.UNDO_LOG日志分析
AT优势
AT模式如何做到对业务的无侵入?
—阶段步骤:
TM: business-service.buy(long,long)方法执行时,由于该方法具有@GlobalTranscational 标志,该TM会向TC发起全局事务,生成XID(全局锁)
RM:OnderService.create(long,long):写表,UNDO_LOG记录回滚日志(Branch lD),通知TC操作结果
RM: StorageService.changelMum(long,long):写表,UNDO_LOG记录回滚日志(Branch lD),通知TC操作结果
RM写表的过程,Seata会拦截业务sQL,首先解析SQL语义,在业务数据被更新前,将其保存成before image,然后执行业务SQL,在业务数据更新之后,再将其保存成after image,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
二阶段步骤:
因为"业务SQL"在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
·正常:TM执行成功,通知TC全局提交,TC此时通知所有的RM提交成功,删除UNDO_LOG回滚日志
异常:TM执行失败,通知TC全局回滚,TC此时通知所有的RM进行回滚,根据UNDO_LOG反向操作,使用beforeimage还原业务数据,删除UNDO_LOG,但在还原前要首先要校验脏写,对比"数据库当前业务数据"和"after
image"”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
AT模式的一阶段、二阶段提交和回滚均由Seata框架自动生成,用户只需编写业务sQL,便能轻松接入分布式事务,AT模式是一种对业务无任何侵入的分布式事务解决方案。
读写隔离
1.写隔离
。一阶段本地事务提交前,需要确保先拿到全局锁。。拿不到全局锁,不能提交本地事务。
。拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
以一个示例来说明:
两个全局事务tx1和tx2,分别对a表的m字段进行更新操作,m的初始值1000。
tx1先开始,开启本地事务,拿到本地锁,更新操作m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的全局锁,本地提交释放本地锁。ts2后开始,开启本地事务,拿到本地锁,更新操作m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的全局锁,tx1全局提交前,该记录的全局锁被tx1持有,tx2需要重试等待全局锁。
tx1 二阶段全局提交,释放 全局锁 。tx2 拿到 全局锁 提交本地事务
如果tx1的二阶段全局回滚,则tx1需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
此时,如果tx2仍在等待该数据的全局锁,同时持有本地锁,则tx1的分支回滚会失败。分支的回滚会一直重试,直到tx2的全局锁等锁超时,放弃全局锁并回滚本地事务释放本地锁,tx1的分支回滚最终成功。
因为整个过程全局锁在tx1结束前一直是被tx1持有的,所以不会发生脏写的问题。
⒉读隔离
在数据库本地事务隔离级别读已提交(Read Committed)或以上的基础上,Seata(AT模式)的默认全局隔离级别是读未提交(Read Uncommitted) 。
如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理
SELECT FOR UPDATE语句的执行会申请全局锁,如果全局锁被其他事务持有,则释放本地锁(回滚SELECT FORUPDATE语句的本地执行)并重试。这个过程中,查询是被block住的,直到全局锁拿到,即读取的相关数据是已提交的,才返回。
出于总体性能上的考虑,Seata目前的方案并没有对所有SELECT语句都进行代理,仅针对 FOR UPDATE的 SELECT语句。
for update扩展
1.使用场景
如果遇到存在高并发并且对于数据的准确性很有要求的场景,需要使用for update。
比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据—直执行下去,就会有问题。所以需要for upate进行数据加锁防止高并发时候数据出错。
for update仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。
要测试for update的锁表情况,可以利用MySQL的Command Mode,开启二个视窗来做测试。
3.总结
o for update操作在未获取到数据的时候,mysql不进行锁(no lock)
。获取到数据的时候,进行对约束字段进行判断,存在有索引的字段则进行row lock否则进行table lock
。当使用’< > ', like’等关键字时,进行for update操作时,mysql进行的是table lock
第四章:TCC模式详解
TCC模式需要用户根据自己的业务场景实现 Try、Confim和Cancel三个操作;事务发起方在一阶段执行Try方式,在二阶段提交执行Confirm方法,二阶段回滚执行Cancel方法。
TCC 三个方法描述:
Try:资源的检测和预留;
Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
Cancel:预留资源释放;
TCC 的实践经验
蚂蚁金服TCC实践,总结以下注意事项:
≥业务模型分2阶段设计≥并发控制≥允许空回滚≥防悬挂控制一幂等控制
1TCC设计-业务模型分﹖阶段设计:用户接入TCC,最重要的是考虑如何将自己的业务模型拆成两阶段来实现。
以"扣钱"场景为例,在接入TCC前,对A账户的扣钱,只需一条更新账户余额的SQL便能完成;但是在接入TCC之后,用户就需要考虑如何将原来一步就能完成的扣钱操作,拆成两阶段,实现成三个方法,并且保证一阶段Try成功的话二阶段Confirm —定能成功。
如上图所示,Try方法作为一阶段准备方法,需要做资源的检查和预留。在扣钱场景下,Try 要做的事情是就是检查账户余额是否充足,预留转账资金,预留的方式就是冻结A账户的转账资金。Try 方法执行之后,账号A余额虽然还是100,但是其中30元已经被冻结了,不能被其他事务使用。
二阶段Confirm方法执行真正的扣钱操作。Confirm 会使用 Try阶段冻结的资金,执行账号扣款。Confirm方法执行之后,账号A在一阶段中冻结的30元已经被扣除,账号A余额变成70元。
如果二阶段是回滚的话,就需要在Cancel方法内释放一阶段Try 冻结的30元,使账号A的回到初始状态,100元全部可用。
用户接入TCC模式,最重要的事情就是考虑如何将业务模型拆成2阶段,实现成TCC的3个方法,并且保证Try成功Confirm一定能成功。相对于AT模式,TCC模式对业务代码有一定的侵入性,但是TCC模式无AT模式的全局行锁,TCC性能会比AT模式高很多。
2 TCC 设计 - 允许空回滚
3 TCC 设计 - 防悬挂控制
4 TCC 设计 - 幂等控制
第五章:Seata Saga模式详解
Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。
Saga 正向服务与补偿服务也需要业务开发者实现。因此是业务入侵的。
Saga模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga模式是一种长事务解决方案。
Saga模式使用场景
Saga模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。
事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供TCC要求的接口,可以使用Saga模式。
Saga模式的优势是:
.—阶段提交本地数据库事务,无锁,高性能;·参与者可以采用事务驱动异步执行,高吞吐;
·补偿服务即正向服务的"反向”,易于理解,易于实现;
缺点: Saga模式由于一阶段已经提交本地数据库事务,且没有进行"预留"动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。
与TCC实践经验相同的是,Saga模式中,每个事务参与者的冲正、逆向操作,需要支持:
·空补偿:逆向操作早于正向操作时;
·防悬挂控制:空补偿后要拒绝正向操作
·幂等