分布式事务 - 分布式事务框架 Seata
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。它提供了AT、TCC、Saga和XA事务模式,为开发者提供了一站式的分布式事务解决方案。其中TCC和XA可以参考之前的文章,AT和Saga这两种事务模型是什么呢?
AT模式
AT模式是Seata最主推的分布式事务解决方案,它是基于XA演进而来的一种分布式事务模式,所以它同样分为三大模块,分别是TM、RM和TC,其中TM和RM作为Seata的客户端与业务系统集成,TC作为Seata的服务器独立部署。TM表示事务管理器(Transaction Manager),它负责向TC注册一个全局事务,并生成一个全局唯一的XID。在AT模式下,每个数据库资源被当作一个RM(Resource Manager),在业务层面通过JDBC标准的接口访问RM时,Seata会对所有请求进行拦截。每个本地事务进行提交时,RM都会向TC(Transaction Coordinator,事务协调器)注册一个分支事务。Seata的AT事务模式如下图所示:
具体执行流程如下:
TM向TC注册全局事务,并生成全局唯一的XID。
RM向TC注册分支事务,并将其纳入该XID对应的全局事务范围。
RM向TC汇报资源的准备状态。
TC汇总所有事务参与者的执行状态,决定分布式事务是全部回滚还是提交。
TC通知所有RM提交/回滚事务。
AT模式和XA一样,也是一个两阶段提交事务模型,不过和XA相比,做了很多优化,后面可以详细介绍。
Saga模式
Saga模式又称为长事务解决方案,主要描述的是在没有两阶段提交的情况下如何解决分布式事务问题。其核心思想是:把一个业务流程中的长事务拆分为多个本地短事务,业务流程中的每个参与者都提交真实的提交给该本地短事务,当其中一个参与者事务执行失败,则通过补偿机制补偿前面已经成功的参与者。
如下图所示,Saga由一系列sub-transaction Ti组成,每个Ti都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的数据变更结果。它和TCC相比,少了Try这个预留动作,每一个Ti操作都真实地影响到数据库。
按照Saga的工作模式,有两种执行方式:
T1,T2,T3,… Ti:这种方式表示所有事务都正常执行。
T1,T2,…Tj,Cj,…,C2,C1(其中0<j<i):这种方式表示执行到Tj事务时出现异常,通过补充操作撤销之前所有成功的sub-transaction。
另外,Saga提供了以下两种补偿恢复方式:
向后恢复,也就是上面提到的第二种工作模式,如果任一子事务执行失败,则把之前执行的结果逐一撤销。
向前恢复,也就是不进行补偿,而是对失败的事务进行重试,这种方式比较适合于事务必须要执行成功的场景。
不管是向后恢复还是向前恢复,都可能出现失败的情况,在最坏的情况下只能人工干预处理。
Saga的优劣势
和XA或者TCC相比,它的优势包括:一阶段直接提交本地事务;没有锁等待,性能较高;在事件驱动的模式下,短事务可以异步执行;补偿机制的实现比较简单。
缺点是Saga并不提供原子性和隔离性支持,隔离性的影响是比较大的,比如用户购买一个商品后系统赠送一张优惠券,如果用户已经把优惠券给使用了,那么事务如果出现异常要回滚时就会出现问题。
Saga的实现方式
在一个电商平台的下单场景中,一般会涉及订单的创建、商品库存的扣减、钱包支付、积分赠送等操作,整体的时序图如下图所示:
电商平台下单的流程时一个典型的长事务场景,根据Saga模式的定义,先将长事务拆分成多个本地短事务,每个服务的本地事务按照执行顺序逐一提交,一旦其中一个服务的事务出现异常,则采用补偿的方式逐一撤回。这一过程的实现会涉及Saga得到协调模式,它有两种常用的协调模式:
事件/编排式:把Saga的决策和执行顺序逻辑分布在Saga的每一个参与者中,他们通过交换事件的方式来进行够通。
命令/协同式:把Saga的决策和执行顺序逻辑集中在一个Saga控制类中,它以命令/回复的方式与每项服务进行通信,告诉它们应该执行哪些操作。
事件/编排式
在基于事件的编排模式中,第一个服务执行完一个本地事务之后,发送一个事件。这个事件会被一个或者多个服务监听,监听到事件的服务再执行本地事务并发布新的事件,此后一直延续这种事件触发模式,直到该业务流程中最后一个服务的本地事务执行结束,才意味着整个分布式长事务也执行结束,如下图所示:
这个流程看起来很复杂,但是却是比较常见的解决方案,下面简单描述一下具体的步骤:
订单服务创建新的订单,把订单状态设置为待支付,并发布一个ORDER_CREATE_EVENT事件。
库存服务监听到ORDER_CREATE_EVENT事件后,执行本地的库存冻结方法,如果执行成功,则发布一个ORDER_PREPARED_EVENT事件。
支付服务监听ORDER_PREPARED_EVENT事件后,执行账户扣款方法,并发布PAY_ORDER_EVENT事件。
最后,积分服务监听PAY_ORDER_EVENT事件,增加账户积分,并更新订单状态为成功。
上述任一步骤执行失败,都会发送一个失败的事件,每个服务需要监听失败的情况根据实际需求进行逐一回滚。
命令/协同式
命令/协同式需要定义一个Saga协调器,负责告诉每一个参与者该做什么,Saga协调器以命令/回复得到方式与每项服务进行通信,如下图所示:
命令/协同式的实现步骤如下:
订单服务首先创建一个订单,然后创建一个订单Saga协调器,启动订单事务。
Saga协调器向库存服务发送冻结库存命令,库存服务通过Order Saga Reply Queue回复执行结果。
接着,Saga协调器继续向支付服务发起账户扣款命令,支付服务通过Order Saga Reply Queue回复执行结果。
需要注意的是,订单Saga协调器必须提前知道“创建订单事务”的所有流程,并且在整个流程中任何一个环节执行失败,它都需要向每个参与者发送命令撤销之前的事务操作。
版权声明: 本文为 InfoQ 作者【Java收录阁】的原创文章。
原文链接:【http://xie.infoq.cn/article/ba1a170f249060b2b311fde10】。文章转载请联系作者。
评论