前面在微服务里整合了Seata,下面利用Seata去解决分布式事务的问题,回去学习Seata中的四种解决方案 :首先学习XA模式
(1)XA模式
RM在前面讲的是资源管理器,在XA标准中RM都是由数据库来实现的,数据库本身实现了这个功能
XA是分布时事务领域最早的一个标准,几乎主流的数据库都实现了这个标准
XA是基于数据库本身的特性来去实现分布式事物的,是能够满足ACID的特性的,它是一种强一致性的事务
Seata实现的 是在数据库xa模式基础上做了一层封装,实现起来是比较简单的 ,它多了一个TM做整个事务的一个注册个管理,变的健壮
优势:
1.在第一阶段只是执行事务而不提交,事务一直处于运行中的一个状态的,事务本身具备ACID的特性,到了第二阶段,等到所有的分支事务都执行完了然后才一起提交,因为这个特性,每个事务都是ACID的,并且大家相互等待,因此整个事务都能实现ACID,认为这种XA具备强一致性的
2.数据库本身都实现了XA模式,数据库本身已经实现了对应的接口,你调用我我去执行,实现各种各样的功能,它是比较容器去实现分布式的效果的,Seata实现这种XA模式的时候也仅仅是在数据库接口上做了一层简单的封装形成RM,用起来非常的简单
缺点:
1.第一阶段不提交,等着第二阶段在提交,在等的时候占用数据库的锁,比如一个事务跨越了四五个更多的事务,每个事务执行完了还得等着别人,如果这个业务耗时较长,四五个分支都执行完耗时很久,整个事务过程中不提交占用系统资源,占用数据库锁,被人都不能访问,是一种资源的浪费,性能比较差,可用性低
2.这种方式依赖数据库底层的实现的,用起来比较简单了,但是如果数据库不支持比如Redis库,就没有办法做了
把数据源形成了一个代理,将来发起的业务sql,都会被Seata(RM)拦截下来,内部调用数据的这种接口,从而实现XA模式,数据源的配置,在参与全局事务的微服务里都需要去添加
给去全局事务的入口方法添加全局事务的注解,全局事务都由一个入口,这个入口要把它标记起来,这样我们的Seata就知道去全局事务是从哪里开始的,就会用TM代理这个方法,从这个方法开始,调用了哪些微服务我们的Seata就知道了,一个微服务的参与者就是一个分支事务了这样去全局事务的边界就定义下来了 ,这个注解只要在入口添加
(2)XA模式实现
在每个微服务里添加:XA配置
在OrderService中的入口方法添加注解:@GlobalTransation
重启微服务
在Seata控制台里服务会重新注册:
现在的数据库:
Order表
account表
storage表:
发起订单当余额充足,数量充足的时候:
数据库表正常减少:
库存-2
越-2
订单新增:
当购买数量超过剩余的:
去看数据库会不会回滚:发现数据库还是原样没有发生变化,发生了回滚
查看日志:库存报错了
余额执行了回滚
(3)AT模式
AT模式跟XA模式的区别是执行完业务sql,而是直接提交不是等待执行,就没有资源的等待了,直接释放了,性能就会提高
虽然提交了,但是怎么保持数据状态的一致性呢?它会在提交前后形成一个快照,如果有问题及时回滚原样
(4)AT模式存在的问题
AT相比XA模式性能得到了提高,但是正是由于提前释放了资源,没有去做锁,这就导致了在并发访问的时候,会存在一些安全问题
会存在并发访问是,丢失更新的问题,事务一回滚后的是100,相当于事务二没有进行操作
这是因为事务没有存在隔离,事务2插入进来,事务一阶段二运行的时候出现脏数据,归根是隔离的原因,比如说阶段一和阶段二都是一个锁定状态,别人没有办法插入进来,就不会出现问题
为了解决这个问题AT模式引入全局锁
虽然说加了全局锁,但是AT模式的性能依赖比XA模式好,XA是数据库的锁,锁不释放,所有人不能访问数据,因为全局锁记录操作这张表的全局事务有Seata来管理的,如果其他事务(比如修改的是其他字段)不是有Seata来管理的 是没有影响的,AT全局锁锁定的范围是比较小的,所以性能较好
但是可能会有极端的情况 当你在修改money的过程中,有一个其他的事务没有由Seata来管理,这个时候他不需要获取全局锁也会出现这种问题,只是这种可能较低
原因:1.因为全局事务大多数执行是成功的 2.分布式事务耗时比较长,因此它的并发比较低,我们在业务上也会尽量避免多个事务去操作了同一个字段
AT对于这种极少的情况也可以这样解决:
事务一会拿更新后的快照,跟当前的数据库的值作一个对比,如果一样则证明没有其他事务更改过,可以进行回滚,不一样说明有事务做了更改Seata就知道不能修改了,这个时候需要人工介入解决
(5)AT模式实现
全局锁表lock_table 快照表undo_log
在seata相关的库中中导入
在于微服务相关的库seata_demo中新建:
更改每个微服务的模式:AT
重启服务:查看数据库表
发起异常订单:
然后查询数据库还是原样
新建的表也是临时存储一下数据,事务回滚完也就删除了,打断点可以看到表中的数据