事务某些业务需求,一系列操作必须全部执行,不能只执行一部分。例如转账操作:--从id=1的账户转100元到id=2的账户--第一步:从id=1的账户A的余额中减去100UPDATEaccountsSETbalance=balance-100WHEREid=1;--StepStep2:添加100UPDATEaccounts到id=2的账户B的余额中SETbalance=balance+100WHEREid=2;这两条SQL语句必须全部执行,或者由于某种原因,如果第一条语句成功,第二条语句失败,则必须全部撤销。这种将多个语句作为一个整体进行操作的能力称为数据库事务。数据库事务可以保证事务范围内的所有操作可以全部成功或全部失败。如果事务失败,那么效果就和没有执行这些SQL一样,不会对数据库数据做任何改变。微服务如果一个事务涉及的所有操作都可以放在一个服务里面,那么使用各种语言的事务相关库就可以很容易的实现多个操作作为一个整体的事务操作。但是有些服务,比如生成订单,涉及到很多操作,包括库存、优惠券、赠品、账户余额等等,当系统的复杂度增加时,如果要把这些操作全部放到一个服务中,那么耦合度就会是太高了,维护成本会很高。对于复杂的系统,目前流行的微服务架构是一个很好的解决方案。这种架构可以将复杂的系统进行拆分,拆分后形成大量的微服务,可以独立开发和维护。虽然业务是拆分的,但订单本身的逻辑需要将多个操作作为一个整体,要么全部成功,要么全部失败,这就带来了新的挑战。如何将分散在各个微服务中的局部事务组合成一个大事务,保证它们作为一个整体集成在一起,是分布式事务需要解决的问题。分布式事务分布式事务简单的说就是一个大的操作是由不同的小操作组合而成的。这些小操作分布在不同的服务器上,属于不同的应用。分布式事务需要保证这些小操作全部成功,否则全部失败。本质上,分布式事务是为了保证不同数据库中数据的一致性。分布式事务方案包括:xatccsagaReliablemessage我们先来看一个最简单的xaXAXA是X/Open组织提出的分布式事务规范。XA规范主要定义了(全局)事务管理器(TM)和(本地)资源管理器(RM)之间的接口。mysql等本地数据库在XA中起到了RM的作用。XA分为两个阶段:第一阶段(prepare):即所有参与者RM准备执行事务并锁定所需资源。当参与者准备就绪时,它会向TM报告它已准备就绪。第二阶段(commit/rollback):当事务管理器(TM)确认所有参与者(RM)准备就绪后,向所有参与者发送commit命令。目前主流的数据库基本都支持XA事务,包括mysql、oracle、sqlserver、postgre等。看看本地数据库是如何支持XA的:第一阶段准备XAstart'4fPqCNTYeSG'UPDATE`user_account`SET`balance`=balance+30,`update_time`='2021-06-0911:50:42.438'WHEREuser_id='1'XAend'4fPqCNTYeSG'XAprepare'4fPqCNTYeSG'--当所有参与者都完成prepare后,进入第二阶段提交xacommit'4fPqCNTYeSG'xa实践介绍了这么多,下面我们在微服务上实践完成一个xa事务加深理解分布式事务。这里使用dtm作为分布式事务的管理器,运行其中一个xa的demo,安装go,安装mysql,获取dtmgitclonehttps://github.com/yedf/dtm.gitcddtm配置mysqlcpconf.sample.ymlconf.ymlviconf。yml运行示例gorunapp/main.goxa从日志中可以找到XA部分的输出,最终成功提交并完成交易#Service1OutputXAstart'4fPqCNTYeSG'UPDATE`user_account`SET`balance`=balance-30,`update_time`='2021-06-0911:50:42.438'WHEREuser_id='1'XAend'4fPqCNTYeSG'XAprepare'4fPqCNTYeSG'#Service2OutputXAstart'4fPqCPijxyC'UPDATE`user_account`SET`balance`=balance+30,`update_time`='2021-06-0911:50:42.493'WHEREuser_id='2'XAend'4fPqCPijxyC'XAprepare'4fPqCPijxyC'#Service1outputxacommit'4fPqCNTYeSG'#Service2outputxacommit'4fPqCPijxyC'时序整个交互细节如下://微服务处理函数:app.POST(BusiAPI+"/TransInXa",common.WrapHandler(func(c*gin.Context)(interface{},error){returnXaClient.XaLocalTransaction(c.Request.URL.Query(),func(db*sql.DB,xa*dtmcli.Xa)(interface{},error){_,err:=dtmcli.DBExec(db,"updatedtm_busi.user_accountsetbalance=balance+?whereuser_id=?",reqFrom(c).Amount,2)returntmcli.MapSuccess,err})}))app.POST(BusiAPI+"/TransOutXa",common.WrapHandler(func(c*gin.Context)(interface{},error){returnXaClient.XaLocalTransaction(c.Request.URL.Query(),func(db*sql.DB,xa*dtmcli.Xa)(interface{},error){_,err:=dtmcli.DBExec(db,"updatedtm_busi.user_accountsetbalance=balance-?whereuser_id=?",reqFrom(c).Amount,1)returntmcli。MapSuccess,err})}))//开启XA事务err:=XaClient.XaGlobalTransaction(gid,func(xa*dtmcli.Xa)(*resty.Response,error){resp,err:=xa.CallBranch(&TransReq{Amount:30},Busi+"/TransOutXa")iferr!=nil{returnresp,err}returnxa.CallBranch(&TransReq{Amount:30},Busi+"/TransInXa")})至此,对xa分布式事务的完整介绍完成关于分布式事务更全面的知识,请参考分布式事务最经典的七大解决方案。在这篇简短的文章中,我们简要介绍了事务、分布式事务和微服务来处理XA事务。有兴趣的同学可以通过dtm继续研究分布式事务。
