2020-07-27

2020-07-27  本文已影响0人  張无忌

Seata解决方案整体介绍

1.SEATA是什么:

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 ATTCCSAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

2、SEATA整体框架:

     Seata 中有三大模块,分别是 TM、RM 和 TC。其中 TM 和 RM 是作为 Seata 的客户端与业务系统集成在一起,TC 作为 Seata 的服务端独立部署。

角色划分:

TM事务管理器,开启、 提交、回滚分布式事务

RM: 资源管理器,注册、 汇报、执⾏资源

TC : 事务管理器服务功能,存储事务日志、补偿异常事务等、集中管理事务全局锁(全局行锁),seata服务端

事务执行整体流程:

TM 开启分布式事务(TM 向 TC 注册全局事务记录);

按业务场景,编排数据库、服务等事务内资源(RM 向 TC 汇报资源准备状态 );

TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);

TC 汇总事务信息,决定分布式事务是提交还是回滚;

TC 通知所有 RM 提交/回滚 资源,事务二阶段结束;

3、AT模式介绍

AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

1、 整体机制:

   两阶段提交协议的演变:

   一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

   二阶段:

提交异步化,非常快速地完成。

回滚通过一阶段的回滚日志进行反向补偿。

2、详细流程:

一阶段提交:

  在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

 二阶段提交

   二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

 二阶段回滚:

     二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

 3、并发事务的隔离处理:  

 写隔离:

       正常提交:

 tx1先开启本地事务,拿到本地锁; tx1更新操作 m = 1000-100 = 900; tx1拿到记录的全局行锁 ; tx1本地提交,释放本地锁  tx2开启本地事务,拿到本地锁; tx2更新操作 m = 900-100 = 800。 tx2尝试拿该记录的全局行锁; tx2重试等待全局锁 (tx1全局提交前,全局锁被 tx1 持有)  tx1全局提交,释放全局锁; tx2拿到记录的全局行锁; tx2本地提交,释放本地锁; tx2全局提交,释放全局行锁。

 数据回滚:

tx1获得全局锁,二阶段全局回滚,需获取本地锁; tx2持有本地锁,等待全局行锁。  tx1分支回滚失败,一直重试;  tx2等待全局行锁超时; tx2回滚本地事务,释放本地锁; tx1获取本地锁,回滚成功。 tx1全局回滚,释放全局行, 整个过程全局锁一直被 tx1 持有,所以不会发生脏写的问题。 

   读隔离:

       AT 模式默认全局隔离级别是读未提交 。如果应用在特定场景下,必需全局读已提交,Seata方式是通过 SELECT FOR UPDATE 语句代理。  SELECT FOR UPDATE 语句执行会自动申请全局行锁,如果全局锁被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。  这个过程中,查询是被 block 住的,直到全局行锁拿到,即读取的相关数据是已提交的才返回。

4、适用场景:

   分布式事务的业务逻辑中仅仅纯数据库操作,不包含其他中间件事务逻辑。

 优势:

   改动及代码侵入最小。由Seata来负责Commit和Rollback的自动化触发或回滚操作。

   劣势

   1、如果事务中包含缓存存储或发送EQ消息等不适合。

   2、为了保证镜像sql的可靠性,需要用户对sql尽量做简化, 建议做法:将多条SQL语句分解为多个事务中的原子步骤(对应SeataAt模式的分支Branch概念),如果单条SQL语句跨表,也分解成为多个事务中的原子步骤(尽量降低Seata存储镜前SQL结果时的风险)。

   3、多次对DB的操作,以及全局行锁的存在对并发处理性能有影响。

4、TCC模式介绍

 该模式由蚂蚁金服贡献。TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

整体流程图:

TCC 三个方法描述:

Try:资源的检测和预留;

Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;

Cancel:预留资源释放;

TCC典型业务场景分析:

转账场景:A账户扣钱,B账户加钱;

账户 A 上有 100 元,要扣除其中的 30 元 :

如上图所示,Try 方法作为一阶段准备方法,需要做资源的检查和预留。

在扣钱场景下,Try 要做的事情是就是检查账户余额是否充足,预留转账资金,预留的方式就是冻结 A 账户的 转账资金。Try 方法执行之后,账号 A 余额虽然还是 100,但是其中 30 元已经被冻结了,不能被其他事务使用。         二阶段 Confirm 方法执行真正的扣钱操作。Confirm 会使用 Try 阶段冻结的资金,执行账号扣款。Confirm 方法执行之后,账号 A 在一阶段中冻结的 30 元已经被扣除,账号 A 余额变成 70 元 。

      如果二阶段是回滚的话,就需要在 Cancel 方法内释放一阶段 Try 冻结的 30 元,使账号 A 的回到初始状态,100 元全部可用。

TCC 设计要点:

   允许空回滚:

    状态: try未执行,收到cancel请求

    原因:

Try超时(网络丢包)---->分布式事务回滚,触发cancel---->未收到try,收到cancel

    解决方案:

    Cancel 接口设计时需要允许空回滚。在 Try 接口因为丢包时没有收到,事务管理器会触发回滚,这时会触发         Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。让事务服务管理器       认为已回滚。

    防悬挂控制:

      状态:Cancel比Try先执行

     原因:

Try超时(网络拥堵)------> 分布式事务回滚,触发cancel----->拥堵的Try到达

    解决方案:

    首先Cancel 接口设计时需要允许空回滚。在 Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行Try 的业务操作。

   幂等处理:

     Try、Confirm、Cancel三个方法均要保证幂等性。

TCC适用场景:

     分布式事务的业务逻辑中除了数据库操作外,包含其他中间件事务逻辑。

优势:

    1、用户可以自己定义业务的补偿逻辑,由业务层保证事务的一致性;

    2、适合微服务化场景;

    3、无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。

劣势:

     1、需要考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,并且保证 Try 成功 Confirm 一定能成功,Confirm失败会不断重试。

     2、TCC 模式下开发者需要自行实现try,confirm,cancel接口,对业务代码有一定的侵入性。

4、Seata的saga模式

Saga 模式介绍:

      Saga 模式是Seata的长事务解决方案,由蚂蚁金服主要贡献。在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

Saga模式机制:

      分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

Saga设计要点:

允许空补偿:

      状态:原服务未执行,补偿服务执行了。

      原因:

              原服务超时(丢包)---> Saga事务触发回滚 ---->  未收到原服务的请求,先收到补偿请求。

      解决方案:允许空补偿。

防悬挂控制

       状态:补偿服务比原服务先执行。

       原因:

                原服务超时(网络拥堵)-----> Saga事务回滚-----> 先进行补偿操作以后,拥堵的原服务到达。

      解决方案:允许空回滚,拒绝空回滚以后原服务的执行。

幂等控制:

      原服务和补偿服务支持幂等操作。

自定义事务恢复策略:

      1、Saga事务不保证隔离性,在极端情况下会出现由于脏写导致无法完成回滚补偿操作,此时状态机引擎可以“重试”继续往前完成这个分布式事务。由于整个业务流程是由状态机编排的,即使是事后恢复也可以继续往前重试。

     2、 用户可以基于业务特点配置该流程的事务处理策略是优先“回滚”还是“重试”,当事务超时,seata会基于用户配置的策略不断进行重试。

使用建议:

        由于 Saga 不保证隔离性,所以我们在业务设计的时候需要做到“宁可长款,不可短款”的原则,长款是指在出现差错的时候站在我方的角度钱多了的情况,钱少了则是短款,因为如果长款可以给客户退款,而短款则可能钱追不回来了,也就是说在业务设计的时候,一定是先扣客户帐再入帐,如果因为隔离性问题造成覆盖更新,也不会出现钱少了的情况。

适用场景:

      1、长流程事务、不需马上返回最终结果,只要保证最终一致性的场景,

      2、对数据隔离性要求不高,对性能要求高的场景。

      3、事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口。

优势:

 事件驱动、无全局锁、异步、高吞吐、性能高,补偿易实现。

劣势:

      自己写补偿逻辑,没有事务隔离性。

上一篇下一篇

猜你喜欢

热点阅读