recover是什么意思

更新时间:2023-01-03 12:21:13 阅读: 评论:0


2023年1月3日发(作者:滕州一中)

事务回滚什么意思try_分布式事务之解决⽅案(TCC)

4.分布式事务解决⽅案之TCC

4.1.什么是TCC事务

TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分⽀事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。Try操

作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现⼀个与Try相反的操作既回滚操作。TM⾸先发起所有的分⽀事务的try操

作,任何⼀个分⽀事务的try操作执⾏失败,TM将会发起所有分⽀事务的Cancel操作,若try操作全部成功,TM将会发起所有分⽀事务的

Confirm操作,其中Confirm/Cancel操作若执⾏失败,TM会进⾏重试。

分⽀事务失败的情况:

TCC分为三个阶段:

阶段是做业务检查(⼀致性)及资源预留(隔离),此阶段仅是⼀个初步操作,它和后续的Confirm⼀起才能真正构成⼀个完整的

业务逻辑。

m阶段是做确认提交,Try阶段所有分⽀事务执⾏成功后开始执⾏Confirm。通常情况下,采⽤TCC则认为Confirm阶段是不

会出错的。即:只要Try成功,Confirm⼀定成功。若Confirm阶段真的出错了,需引⼊重试机制或⼈⼯处理。

阶段是在业务执⾏错误需要回滚的状态下执⾏分⽀事务的业务取消,预留资源释放。通常情况下,采⽤TCC则认为Cancel阶段

也是⼀定成功的。若Cancel阶段真的出错了,需引⼊重试机制或⼈⼯处理。

事务管理器

TM事务管理器可以实现为独⽴的服务,也可以让全局事务发起⽅充当TM的⾓⾊,TM独⽴出来是为了成为公⽤组件,是为了考虑结构

和软件复⽤。

TM在发起全局事务时⽣成全局事务记录,全局事务ID贯穿整个分布式事务调⽤链条,⽤来记录事务上下⽂,追踪和记录状态,由于

Confirm和Cancel失败需进⾏重试,因此需要实现为幂等性是指同⼀个操作⽆论请求多少次,其结果都相同。

解决⽅案

⽬前市⾯上的TCC框架众多⽐如下⾯这⼏种:

Seata也⽀持TCC,但Seata的TCC模式对SpringCloud并没有提供⽀持。我们的⽬标是理解TCC原理以及事务协调运作的过程,因此更

倾向于轻量级易于理解的框架。

Hmily是⼀个⾼性能分布式事务TCC开源框架。基于Java语⾔来开发(JDK1.8),⽀持Dubbo,SpringCloud等RPC框架进⾏分布式事

务。它⽬前⽀持以下特性:

⽀持嵌套事务(Nestedtransactionsupport)。

采⽤disruptor框架进⾏事务⽇志的异步读写,与RPC框架的性能毫⽆差别。

⽀持SpringBoot-starter项⽬启动,使⽤简单。

RPC框架⽀持:dubbo、motan、springcloud。

本地事务存储⽀持:redis、mongodb、zookeeper、file、mysql。

事务⽇志序列化⽀持:java、hessian、kryo、protostuff。

采⽤AspectAOP切⾯思想与Spring⽆缝集成,天然⽀持集群。

RPC事务恢复,超时异常恢复等。

Hmily利⽤AOP对参与分布式事务的本地⽅法与远程⽅法进⾏拦截处理,通过多⽅拦截,事务参与者能透明的调⽤到另⼀⽅的Try、

Confirm、Cancel⽅法;传递事务上下⽂;并记录事务⽇志,酌情进⾏补偿,重试等。

Hmily不需要事务协调服务,但需要提供⼀个数据库(mysql/mongodb/zookeeper/redis/file)来进⾏⽇志存储。

Hmily实现的TCC服务与普通的服务⼀样,只需要暴露⼀个接⼝,也就是它的Try业务。Confirm/Cancel业务逻辑,只是因为全局事

务提交/回滚的需要才提供的,因此Confirm/Cancel业务只需要被HmilyTCC事务框架发现即可,不需要被调⽤它的其他业务服务所

感知。

官⽹介绍:/website/zh-cn/docs/hmily/C需要注意三种异常处理分别是空回滚、幂等、悬TCC需要注意三种异常处理分别是空回滚、幂等、悬

挂:挂:空回滚空回滚:

在没有调⽤TCC资源Try⽅法的情况下,调⽤来⼆阶段的Cancel⽅法,Cancel⽅法需要识别出这是⼀个空回滚,然后直接返回成功。

出现原因是当⼀个分⽀事务所在服务宕机或⽹络异常,分⽀事务调⽤记录为失败,这个时候其实是没有执⾏Try阶段,当故障恢复后,

分布式事务进⾏回滚则会调⽤⼆阶段的Cancel⽅法,从⽽形成空回滚。

解决思路是关键就是要识别出这个空回滚。思路很简单就是需要知道⼀阶段是否执⾏,如果执⾏来,那就是正常回滚;如果没执⾏,那

就是空回滚。前⾯已经说过TM在发起全局事务时⽣成全局事务记录,全局事务ID贯穿整个分布式事务调⽤链条。再额外增加⼀张分⽀

事务记录表,其中有全局事务ID和分⽀事务ID,第⼀阶段Try⽅法⾥会插⼊⼀条记录,表⽰⼀阶段执⾏来。Cancel接⼝⾥读取该记录,

如果该记录存在,则正常回滚;如果该记录不存在,则是空回滚。幂等幂等:

通过前⾯介绍已经了解到,为了保证TCC⼆阶段提交重试机制不会引发数据不⼀致,要求TCC的⼆阶段Try、Confirm和Cancel接⼝保

证幂等,这样不会重复使⽤或者释放资源。如果幂等控制没有做好,很有可能导致数据不⼀致等严重问题。

解决思路在上述“分⽀事务记录”中增加执⾏状态,每次执⾏前都查询该状态。悬挂悬挂:

悬挂就是对于⼀个分布式事务,其⼆阶段Cancel接⼝⽐Try接⼝先执⾏。

出现原因是在RPC调⽤分⽀事务try时,先注册分⽀事务,再执⾏RPC调⽤,如果此时RPC调⽤的⽹络发⽣拥堵,通常RPC调⽤是有超

时时间的,RPC超时以后,TM就会通知RM回滚该分布式事务,可能回滚完成后,RPC请求才到达参与者真正执⾏,⽽⼀个Try⽅法预

留的业务资源,只有该分布式事务才能使⽤,该分布式事务第⼀阶段预留的业务资源就再也没有⼈能够处理了,对于这种情况,我们就

称为悬挂,即业务资源预留后⽆法继续处理。

解决思路是如果⼆阶段执⾏完成,那⼀阶段就不能再继续执⾏。在执⾏⼀阶段事务时判断在该全局事务下,“分⽀事务记录”表中是否

已经有⼆阶段事务记录,如果有则不执⾏Try。举例,场景为A转账30元给B,A和B账户在不同的服务。举例,场景为A转账30元给B,A和B账户在不同的服务。⽅案1⽅案1:

账户A

try:检查余额是否够30元扣减30元confirm:空cancel:增加30元

账户B

try:增加30元confirm:空cancel:减少30元

⽅案1说明:

1)账户A,这⾥的余额就是所谓的业务资源,按照前⾯提到的原则,在第⼀阶段需要检查并预留业务资源,因此,我们在扣钱TCC资源的

Try接⼝⾥先检查A账户余额是否⾜够,如果⾜够则扣除30元。Confirm接⼝表⽰正式提交,由于业务资源已经在Try接⼝⾥扣除掉

了,那么在第⼆阶段的Confirm接⼝⾥可以什么都不⽤做。Cancel接⼝的执⾏表⽰整个事务回滚,账户A回滚则需要把Try接⼝⾥扣除

掉的30元还给账户。

2)账号B,在第⼀阶段Try接⼝⾥实现给账户B加钱,Cancel接⼝的执⾏表⽰整个事务回滚,账户B回滚则需要把Try接⼝⾥加的30元再

减去。⽅案1的问题分析:⽅案1的问题分析:

1)如果账户A的try没有执⾏在cancel则就多加了30元。

2)由于try,cancel、confirm都是由单独的线程去调⽤,且会出现重复调⽤,所以都需要实现幂等。

3)账号B在try中增加30元,当try执⾏完成后可能会其它线程给消费了。

4)如果账户B的try没有执⾏在cancel则就多减了30元。问题解决:问题解决:

1)账户A的cancel⽅法需要判断try⽅法是否执⾏,正常执⾏try后⽅可执⾏cancel。

2)try、cancel、confirm⽅法实现幂等。

3)账户B在try⽅法中不允许更新账户⾦额,在confirm中更新账户⾦额。

4)账户B的cancel⽅法需要判断try⽅法是否执⾏,正常执⾏try后⽅可执⾏cancel。优化⽅案:优化⽅案:

账户A:

try:try幂等校验try悬挂处理检查余额是否够30元扣减30元confirm:空cancel:cancel幂等校验cancel空回滚处理增加可⽤余额30

账户B:

try:空confirm:confirm幂等校验正式增加30元cancel:空

实现TCC事务

4.3.1.业务说明

通过Hmily实现TCC分布式事务,模拟两个账户的转账交易过程。

两个账户分别在不同的银⾏(张三在bank1、李四在bank2),bank1、bank2是两个微服务。交易过程是,张三给李四转账制定⾦额。

上述交易步骤,要么⼀起成功,要么⼀起失败,必须是⼀个整体性事务。

4.3.2.程序组成部分

数据库:MySQL-5.7.25

JDK:64位jdk1.8.0_201微服务:spring-boot-2.1.3、EHmily:hmily-springcloud.2.0.4-

RELEASE

微服务及数据库的关系:

dtx/dtx-tcc-demo/dtx-tcc-demo-bank1银⾏1,操作张三账户,连接数据库bank1dtx/dtx-tcc-demo/dtx-tcc-demo-bank2银⾏

2,操作李四账户,连接数据库bank2

服务注册中⼼:dtx/discover-rver

4.3.3.创建数据库

创建hmily数据库,⽤于存储hmily框架记录的数据。

CREATEDATABASEhmilyCHARACTERSET‘utf8’COLLATE‘utf8_general_ci’;创建bank1库,并导⼊以下表结构和数据创建bank1库,并导⼊以下表结构和数据

(包含张三账户)

CREATEDATABASEbank1CHARACTERSET‘utf8’COLLATE‘utf8_general_ci’;创建bank2库,并导⼊以下表结构和数据创建bank2库,并导⼊以下表结构和数据

(包含李四账户)

CREATEDATABASEbank2CHARACTERSET‘utf8’COLLATE‘utf8_general_ci’;

DROPTABLEIFEXISTSaccount_info;CREATETABLEaccount_info(idbigint(20)NOTNULLAUTO_INCREMENT,account_name

varchar(100)CHARACTERSETutf8COLLATEutf8_binNULLDEFAULTNULLCOMMENT‘户主姓名’,account_no

varchar(100)CHARACTERSETutf8COLLATEutf8_binNULLDEFAULTNULLCOMMENT‘银⾏卡号’,account_password

varchar(100)CHARACTERSETutf8COLLATEutf8_binNULLDEFAULTNULLCOMMENT‘帐户密码’,account_balance

doubleNULLDEFAULTNULLCOMMENT‘帐户余额’,

PRIMARYKEY(id)USINGBTREE

)ENGINE=InnoDBAUTO_INCREMENT=5CHARACTERSET=utf8COLLATE=utf8_binROW_FORMAT=Dynamic;

INSERTINTOaccount_infoVALUES(2,‘张三的账户’,‘1’,‘’,10000);

每个数据库都创建try、confirm、cancel三张⽇志表:

CREATETABLE`local_try_log`(`tx_no`varchar(64)NOTNULLCOMMENT`create_time`datetimeDEFAULTNULL,

PRIMARYKEY(`tx_no`))ENGINE=InnoDBDEFAULTCHARSET=utf8CREATETABLE`local_confirm_log`(`tx_no`

varchar(64)NOTNULLCOMMENT`create_time`datetimeDEFAULTNULL)ENGINE=InnoDBDEFAULTCHARSET=utf8

CREATETABLE`local_cancel_log`(`tx_no`varchar(64)NOTNULLCOMMENT`create_time`datetimeDEFAULTNULL,

PRIMARYKEY(`tx_no`))ENGINE=InnoDBDEFAULTCHARSET=utf8

4.3.5⼯程dtx-tcc-demo

(1)引⼊maven依赖

ahmily‐springcloud2.0.4‐

RELEASE

(2)配置hmily

:

org:dromara:hmily:rializer:kryorecoverDelayTime:128retryMax:30scheduledDelay:128scheduledThreadMax:10

repositorySupport:dbstarted:truehmilyDbConfig:driverClassName:url:

jdbc:mysql://localhost:3306/bank?uUnicode=trueurname:rootpassword:root

新增配置类接收中的Hmily配置信息,并创建HmilyTransactionBootstrapBean:

@BeanpublicHmilyTransactionBootstraphmilyTransactionBootstrap(HmilyInitServicehmilyInitService){

HmilyTransactionBootstraphmilyTransactionBootstrap=newHmilyTransactionBootstrap(hmilyInitService);

ializer(perty("izer"));

overDelayTime(nt(perty("rDelayTime")));

ryMax(nt(perty("ax")));

eduledDelay(nt(perty("ledDelay")));

eduledThreadMax(nt(perty("ledThreadMax")));

ositorySupport(perty("torySupport"));

rted(oolean(perty("d")));HmilyDbConfig

hmilyDbConfig=newHmilyDbConfig();

verClassName(perty("ClassName"));

(perty(""));

rname(perty("me"));

sword(perty("rd"));

lyDbConfig(hmilyDbConfig);returnhmilyTransactionBootstrap;}

启动类增加@EnableAspectJAutoProxy并增加的扫描项:

@SpringBootApplication@EnableDiscoveryClient@EnableHystrix@EnableFeignClients(baPackages=

{""})@ComponentScan({"1",""})publicclass

Bank1HmilyServer{publicstaticvoidmain(String[]args){(,args);}}

4.3.7dtx-tcc-demo-bank1

dtx-tcc-demo-bank1实现try和cancel⽅法,如下:

try:try幂等校验try悬挂处理检查余额是够扣减⾦额扣减⾦额confirm:空cancel:cancel幂等校验cancel空回滚处理增加可⽤余额

@Mapper@ComponentpublicinterfaceAccountInfoDao{@Update("updateaccount_infot

account_balance=account_balance-#{amount}whereaccount_balance>=#{amount}andaccount_no=#{accountNo}")int

subtractAccountBalance(@Param("accountNo")StringaccountNo,@Param("amount")Doubleamount);@Update("update

account_infotaccount_balance=account_balance+#{amount}whereaccount_no=#{accountNo}")int

addAccountBalance(@Param("accountNo")StringaccountNo,@Param("amount")Doubleamount);/***增加某分⽀事务try执

⾏记录*@paramlocalTradeNo本地事务编号*@return*/@Inrt("inrtintolocal_try_logvalues(#{txNo},now());")int

addTry(StringlocalTradeNo);@Inrt("inrtintolocal_confirm_logvalues(#{txNo},now());")intaddConfirm(String

localTradeNo);@Inrt("inrtintolocal_cancel_logvalues(#{txNo},now());")intaddCancel(StringlocalTradeNo);/***查询分

⽀事务try是否已执⾏*@paramlocalTradeNo本地事务编号*@return*/@Select("lectcount(1)fromlocal_try_logwhere

tx_no=#{txNo}")intisExistTry(StringlocalTradeNo);/***查询分⽀事务confirm是否已执⾏*@paramlocalTradeNo本地事务编

号*@return*/@Select("lectcount(1)fromlocal_confirm_logwheretx_no=#{txNo}")intisExistConfirm(String

localTradeNo);/***查询分⽀事务cancel是否已执⾏*@paramlocalTradeNo本地事务编号*@return*/@Select("lectcount(1)

fromlocal_cancel_logwheretx_no=#{txNo}")intisExistCancel(StringlocalTradeNo);}

2)try和cancel⽅法

3)feignClient

@FeignClient(value="ata-demo-bank2",fallback=)publicinterfaceBank2Client{

@GetMapping("/bank2/transfer")@HmilyBooleantransfer(@RequestParam("amount")Doubleamount);}

ller

@RestControllerpublicclassBank1Controller{@AutowiredprivateAccountInfoServiceaccountInfoService;

@RequestMapping("/transfer")publicStringtest(@RequestParam("amount")Doubleamount){

AccountBalance("1",amount);return"bank1"+amount;}}

4.3.8dtx-tcc-demo-bank2

dtx-tcc-demo-bank2实现如下功能:

try:空confirm:confirm幂等校验正式增加⾦额cancel:空

1)Dao

@Component@MapperpublicinterfaceAccountInfoDao{@Update("updateaccount_infot

account_balance=account_balance+#{amount}whereaccount_no=#{accountNo}")int

addAccountBalance(@Param("accountNo")StringaccountNo,@Param("amount")Doubleamount);/***增加某分⽀事务try执

⾏记录*@paramlocalTradeNo本地事务编号*@return*/@Inrt("inrtintolocal_try_logvalues(#{txNo},now());")int

addTry(StringlocalTradeNo);@Inrt("inrtintolocal_confirm_logvalues(#{txNo},now());")intaddConfirm(String

localTradeNo);@Inrt("inrtintolocal_cancel_logvalues(#{txNo},now());")intaddCancel(StringlocalTradeNo);/***查询分

⽀事务try是否已执⾏*@paramlocalTradeNo本地事务编号*@return*/@Select("lectcount(1)fromlocal_try_logwhere

tx_no=#{txNo}")intisExistTry(StringlocalTradeNo);/***查询分⽀事务confirm是否已执⾏*@paramlocalTradeNo本地事务编

号*@return*/@Select("lectcount(1)fromlocal_confirm_logwheretx_no=#{txNo}")intisExistConfirm(String

localTradeNo);/***查询分⽀事务cancel是否已执⾏*@paramlocalTradeNo本地事务编号*@return*/@Select("lectcount(1)

fromlocal_cancel_logwheretx_no=#{txNo}")intisExistCancel(StringlocalTradeNo);}

2)实现confirm⽅法

3)Controller

@RestControllerpublicclassBank2Controller{@AutowiredprivateAccountInfoServiceaccountInfoService;

@RequestMapping("/transfer")publicBooleantest2(@RequestParam("amount")Doubleamount){

AccountBalance("2",amount);returntrue;}}

3.3.9测试场景

张三向李四转账成功。

李四事务失败,张三事务回滚成功。

张三事务失败,李四分⽀事务回滚成功。

分⽀事务超时测试。

4.4.⼩结

如果拿TCC事务的处理流程与2PC两阶段提交做⽐较,2PC通常都是在跨库的DB层⾯,⽽TCC则在应⽤层⾯的处理,需要通过业务逻辑来

实现。这种分布式事务的实现⽅式的优势在于,可以让应⽤⾃⼰定义数据操作的粒度,使得降低锁冲突、提⾼吞吐量成为可能应⽤⾃⼰定义数据操作的粒度,使得降低锁冲突、提⾼吞吐量成为可能。

⽽不⾜之处则在于对应⽤的侵⼊性⾮常强,业务逻辑的每个分⽀都需要实现try、confirm、cancel三个操作。此外,其实现难度也⽐较

⼤,需要按照⽹络状态、系统故障等不同的失败原因实现不同的回滚策略。

本文发布于:2023-01-03 12:21:13,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/84392.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:isb是什么意思
下一篇:yan怎么拼读
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图