一、基本信息
1、作用:主要是为了解决分布式架构中的事务问题。
2、分布式事务的基本结构:
TC(Transaction Coordinator)-事务协调者:维护全局事务和分支事务的状态,协调全局事务提交或回滚。
TM(Transaction Manager)-事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务的提交活回滚。
针对三个概念来说,比较抽象,结合下图,了解架构的过程:
分布式事务分为两个阶段:
- 单个微服务的事务
- 整个请求的事务
大致过程:
(1)当请求进来时,事务管理器TM会通知事务协调者TC,需要开启全局事务;
(2)请求走到每一个微服务时,单个微服务内部的业务逻辑会形成分支事务,业务逻辑执行完毕后,资源管理器RM会注册分支事务到事务协调者TC,并且上报分支事务的状态,提交或者回滚。(3)当所有的分支都执行完之后,事务管理器TM会告知事务协调这TC,准备提交全局事务,此时事务协调者需要核验每一个分支事务的上报状态;
(4)如果分支事务全部核验均为提交,在分支事务和全局事务都提交;否则全部回滚。
个人理解:对于分布式事务,我把它叫做事务的事务,由多个事务组成的一个事务。
二、解决方案
1、XA模式:
该模式的过程和上述分布式过程一模一样,两阶段提交模型,数据是强一致性。
优点:
(1)强一致性,满足ACID原则
(2)实现简单,基于数据库的事务,没有代码侵入
缺点:
(1)性能较差,因为分支事务执行完之后不会提交活回滚,因此会占用锁,等待第二阶段才能提交。
(2)依赖数据库事务,如果数据库不支持事务,则无法实现。
2、AT模式:
同样为两阶段提交模型,但是弥补了XA模型中,分支事务长时间挂起不提交或回滚的缺点,避免了事务长时间占用锁,数据是最终一致性。
(1)在第一阶段中,分支事务执行完业务逻辑之后,事务直接提交,此时分支事务会形成一个快照(undo-log),记录事务提交前后的数据。
(2)第二阶段中,TC检查分支事务的上报状态,如果全部成功,则只需要将第一阶段的生成的快照(undo-log)删除即可,因为事务已经在第一阶段提交完了;如果失败,则需要先恢复数据(回滚),然后再删除快照。
AT模式会出现数据脏写的问题:当第一阶段执行完之后,第二阶段执行之前,如果有其他事务对第一阶段的事务所修改的数据再次做了修改,当第二阶段是回滚时,就出现了数据不一致了,其他事物已经将数据更改了,但是回滚却恢复到最开始的数据。举个例子:
id | count |
1 | 100 |
上面这行数据,当分支事务A执行完之后,变为了:
id | count |
1 | 90 |
快照会记录这两组数据,但是此时,又有一个事务B提交了,将数据改为了:
id | count |
1 | 80 |
如果事务A 所在的全局事务执行回滚,那么将会把数据恢复到:
id | count |
1 | 100 |
就会产生数据的不一致;针对这种脏写的问题,可以分为两个方面讨论,就是中途进来更改数据的事务是不是Seata管理的事务:
(1)如果是Seata管理的事务,框架提供了一个全局锁,在第一阶段执行分支事务时,不仅需要获取数据库的DB锁,还需要获取框架的全局锁,全局锁表示在全局事务提交之前,该数据只能由获取全局锁的事务操作,因此,虽然事务提交之后释放了DB锁,被别的事务获取到了,但是别的事务因为被Seata管理,也需要获取这行数据的全局锁,因此导致数据无法被更改;这里有个需要注意的地方,为了避免全局锁和DB锁的死锁问题,全局锁获取时,会有一个重试机制,重试十次,每次10ms,超过这个时间久放弃获取,并且释放DB锁。
(2)如果更改数据的事务不是Seata管理的,那么在操作数据时,就只需要获取DB锁,不需要获取框架的全局锁,那么还是机会存在脏写的问题,虽然几率很小,但是依然存在;此时快照就发挥了作用,快照记录的是数据修改前后的数据,这种情况下在回滚之前,事务会将数据库当前的数据和快照记录的修改后的数据进行对比,如果一致,则恢复,不一致,此时就需要人工干预了,比较麻烦;这种情况的思想和CAS很像,需要比较之后再做修改。
优点:性能比XA好
缺点:数据的最终一致性