1.SEATA是什么:
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 AT、TCC、SAGA 和 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 要求的接口。
优势:
事件驱动、无全局锁、异步、高吞吐、性能高,补偿易实现。
劣势:
自己写补偿逻辑,没有事务隔离性。