由于blog各种垃圾评论太多,而且本人审核评论周期较长,所以懒得管理评论了,就把评论功能关闭,有问题可以直接qq骚扰我

分布式事务之AT方案(Seata实现)

架构设计 西门飞冰 2488℃
[隐藏]

1.什么是分布式事务

分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。

2.为什么会产生分布式事务

场景举例:一个顾客访问某知名电商平台,进行商品购买,电商平台架构按照不同的功能进行了拆分,有订单、会员、库存子系统,在这个下单的过程中分布式事务就产生了。

如下图所示:顾客在创建一个订单的时候,要在订单库增加一个订单数据,同时会同步的会员库增加积分和库存库减少库存。这种操作在单个数据库中完成是没有任何问题的,一旦涉及到了分布式的情况,比如下方三个独立数据库的情况,如何来保障数据全局提交,全局回滚,这就是分布式事务要做的事情了。

image-20221021203845514

3.什么是Seata

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

官方地址:https://seata.io/zh-cn/

4.Seata体系三个角色

事务管理器(TM):决定什么时候全局提交/回滚(司令官)

事务协调者(TC):负责通知命令的中间件Seata-Server(传令官)

资源管理器(RM):做具体事儿的工具人(大头兵)

5.Seata AT 实现流程

5.1.TM:

TM定义了事务的边界,TM就是上面例子中的商城应用,具体作用看下方伪代码,它代表了服务中商城应用的新增订单方法,方法中调用了新增订单的三个具体方法,这三个方法都对应了远程的具体方法,都是发起一个http或者rpc调用。因为创建订单的所有后续处理都在这个新增订单里面,所以说它定义了事务的边界,是一个TM角色。

要开启分布式事务,只需要在TM角色的方法上添加一个@GlobalTransactional注解就可以了,这个是Seata提供的核心注解。

在创建订单的方法开始执行的时候,TM会向TC(我们自己安装的Seata server)发起一个指令,告诉Seata要开始全局事务的工作了

image-20221021210010927

5.2.RM:

@Transactional注解:是sping的声明式事务扩展,Seata对它进行了扩展,支持远程分布式事务

含有@Transactional注解的方法开始执行时会做两件事情:

第一去注册分支事务:如订单服务就是告诉TC 准备要向订单表插入数据了,这就是一个注册分支事务的过程

第二开启本地事务:执行方法的时候所有涉及到这个方法的sql写操作,都是在同一个事务中完成的。

image-20221021211535614

当TM三个远程调用都执行完成之后,@GlobalTransactional会自动向TC发起一个全局事务的提交或者回滚操作。

提交条件:所有分支事务执行成功,下达全局提交操作指令

回滚条件:任何一个分支事务执行失败,下达全局回滚操作指令

image-20221021212858367

image-20221021213039146

6.数据自动提交回滚实现

上面流程有一个问题,就是在Seata执行过程中,本地事务处理完成之后,会立即执行事务提交写表操作。此时表已经写入完成了,本地事务区已经清空,在本地事务执行结束的情况下,如何进行事务的回滚?

Seata解决起来也并不复杂,它需要在所有的TM和RM表中增加一张UNDO_LOG表,也叫回滚日志表,来执行逆向操作。

以刚才订单表举例:用户下了一个订单,作为一个订单服务来说,就是insert into写入一条数据,后续要是库存或者积分服务出现了异常,要进行全局回滚的时候,这个时候Seata就会自动执行UNDO_LOG中自动生成的delete语句,把刚才新增的数据进行删除。

image-20221021222856991

7.Seata如何避免并发场景的脏读与脏写

Seata避免脏读与脏写就是利用TC所自带的分布式锁来完成。

脏写:

如下是一个简单的示意图,作为TC它会为第一个事务增加一个全局的分布式锁,当对ID进行操作的时候,只有持有锁的事务才可以进行操作,要是其他进程要对这个数据进行操作,就会一直处于等待的状态,直到获取到这个锁的时候在去对同一条数据进行后续处理。通过分布式锁在对资源进行锁定,来完成并发写的操作。

image-20221021223559553

注意Seata默认是不会给分布式锁的。

脏读:如果我们对同一条数据进行并发查的时候,同时又有另外一个线程来进行写入,就有可能会造成脏读的现象。

脏读解决方案:要在原有select 语句后面加for update,加上以后Seata就会对这个select语句进行解析,为查找到的数据增加分布式锁,这样即使有其他线程要对这个数据进行写操作的时候,因为要获取分布式锁,所以他会一直处于阻塞状态,只有select执行完毕,分布式锁被释放以后,后续的写操作才会进行执行。

8.Seata AT使⽤要求

所有服务与数据库必须要⾃⼰拥有管理权,因为要创建UNDO_LOG表

最好都是MySQL,听说也⽀持PSQL,不过没试验过

9.应⽤场景:

⾼并发互联⽹应⽤,允许数据出现短时不⼀致,可通过对账程序或补录来保证最终⼀致性。

转载请注明:西门飞冰的博客 » 分布式事务之AT方案(Seata实现)

喜欢 (3)or分享 (0)