106:Seata框架底层深度源码解析
1 回顾seata解决分布式事务的问题
课题内容
- Seata到底和LCN有哪些区别?
- Seata中undo_do日志到底有何作用?
- Seata中的如何逆向生成sql回滚?
- Seata深度源码分析总结
2 seata基于undo_log表逆向生成sql语句回滚
seata解决分布式事务使用逆向生成sql语句回滚,undo_log记录每次的sql执行语句,可能产生脏读,但是可以避免死锁。
场景演示:
LCN参与方等待协调者通知结果过程中一直阻塞等待,此时数据库有begin操作没有commit或者rollback发生行锁,一旦协调者或者发起方宕机,参与方将一直等待从而产生死锁。
3 setata使用前置镜像和后置镜像保存日志信息
Seata实现原理
- TM(发起方)连接到TC事务协调者,创建一个全局的事务id(xid),保存到ThreadLoacl中;
- TM(发起方)和RM(参与方)都被Seata的数据源实现代理,在原生的sql执行之前和之后保存原来的和修改后的结果到undo_log日志中,方便后期实现回滚;
前置镜像和后置镜像
分别表示sql执行之前数据库中的数据结果和执行之后的结果。
select * from orderId=1 state=0 ----前置镜像
update order set state=1 where orderId=1
select * from orderId=1 state=1 ----后置镜像
将前置镜像和后置镜像保存到undo_log日志中
回滚:
update order set state=0 where orderId=1 —还原
4 seata根据undo_log日志逆向回滚原理
- TM(发起方)使用feign客户端调用接口的时候,在ThreadLoacl中获取xid,设置到请求头中;
- RM(参与方)从请求中获取到该xid,设置到ThreadLoacl中,同时也会向Seata server注册该分支事务;
- TM(发起方)将当前本地事务的结果告诉给协调者TC,协调者TC再通知所有的分支是否回滚;
- TM(发起方)如果调用接口成功后抛出异常的情况下,告诉给协调者TC,协调者TC再通知所有的分支根据全局的xid和分支事务的id查询分支数据源的undo_log日志表,逆向生成sql语句实现回滚,同时删除对应的undo_log日志;
- TM(发起方)如果调用接口成功后没有抛出任何的异常,告诉给协调者TC,协调者TC再通知所有的分支根据全局的xid和分支事务的id删除对应的undo_log日志;
如何逆向实现sql语句:
insert 逆向 delete
delete逆向insert
update逆向update(前置镜像和后置镜像)
Seata与Lcn的区别
基本实现的思路是一样的,唯一区别在于回滚的方式
LCN采用代理数据源假关闭连接,暂时不提交本地事务,但是容易造成数据的死锁。
Seata采用undo_log的形式逆向生成sql语句实现回滚,避免死锁现象但是容易出现脏读。
5 seata全局事务注解入口源码类分析
发起方在方法上面加上@GlobalTransactional,被aop拦截。
Aop的入口分析:
- 项目中会引入spring-cloud-alibaba-seata.jar执行spring.factories配置,读取GlobalTransactionAutoConfiguration配置类,会加载全局事务扫描器(GlobalTransactionScanner)到Spring的容器中
- GlobalTransactionScanner实现了AbstractAutoProxyCreator、InitializingBean
AbstractAutoProxyCreator:SpringAOP原生类 创建代理对象
InitializingBean:SpringBean生命周期初始化
GlobalTransactionScanner:对象初始化成功之后开始注册RM和TM到TC中
6 GlobalTransactionalInterceptor源码分析
AbstractAutoProxyCreator类中有一个回调的方法wrapIfNecessar,创建GlobalTransactionalInterceptor。
执行发起方方法前会执行GlobalTransactionalInterceptor的invoke方法,判断方法上是否有加上@GlobalTransactional注解,再执行transactionalTemplate的execute方法。
7 TM如何远程连接TC获取全局事务id
TM流程获取全局事务id流程:
通过当前线程获取xid,如果没有创建默认的全局事务对象GlobalTransaction(里面xid字段为空),事务开启后远程连接TC返回结果中获取xid再设置到threadlocal中。
8 Seata前置和后置镜像源码深度分析
9 Seata发起方调用完接口后发生回滚还是提交
A服务调用B服务接口成功后A服务宕机,B服务已经提交数据库是否需要回滚?
Seata有设置超时时间,在一定时间内如果发起方TM没有告诉TC协调者是否要回滚,参与方RM(B服务)默认自动进行回滚。
数据库seata库branch_table表和global_table表记录该条数据,待A项目再次启动后自动回滚A的数据,同时删除branch_table表和global_table表的记录。