作者:卟抛棄D | 来源:互联网 | 2023-10-11 21:49
Seata和LCN的区别1.基本实现思路是一样的,唯一区别在于回滚方式,LCN采用代理数据源假关闭连接,暂时不提交本地事务(不提交也不回滚),但是容易造成死锁。--假关闭其实就是a
Seata和LCN的区别
1. 基本实现思路是一样的,唯一区别在于回滚方式,LCN采用代理数据源假关闭连接,暂时不提交本地事务(不提交也不回滚),但是容易造成死锁。
-- 假关闭其实就是api,因为连接也是可以被aop代理的,原生的commit被拦截到就不会走commit会进行wait线程等待,等待全局事务协调者发送通知是提交还是回滚。
2. seata采用undo_log的形式逆向生成sql语句实现回滚。
lcn存在问题:容易死锁。
例如:事务协调者宕机,无法通知参与方是提交还是回滚,所以线程会一直被占用(死锁)。
undo_log日志逆向回滚
伪代码:
1 @RequestMapping("/insertUser")
2 @GlobalTransactional
3 public String insertUser(){
4 // 第一步.添加姓名到user表;
5 int i = userMapper.insertName(“王明”);
6
7 // 第二步.调用会员服务修改姓名为王敏;
8 int result = rpcUrl.putName("王敏");
9
10 // 模拟报错
11 int a = 1 / 0;
12 }
使用@GlobalTransactional注解开启事务,
执行完第5行后数据就会提交到user表;
第8行会员服务同样会落实到数据库;
执行到第11行报错后会通过undo_log表逆向回滚数据;
- undo_log表中的rollback_info字段以二进制方式记录着新增的语句,通过该字段进行逆向回滚。
Seata原理分析
- 发起方TM会向我们的TC协调者申请一个全局的事务id,保存到threadlocal中;
- TM和RM都会被Seata代理数据源,在原生的sql之前和之后保存日志到undo_log表中,方便后期实现回滚;
- TM从请求头中传递该全局的事务id给RM,RM从请求中头中获取到该全局事务id,并且注册该分支。
- 如果TM调用接口成功之后,如果报错的情况下则通知给协调者,协调者在告诉所有的分支都开始回滚,直接根据本地事务id+xid查询undo_log表 ,逆向生成sql语句回滚,同时删除该undo_log日志。
- 如果TM调用接口成功之后,如果没有报错的情况下则通知给协调者,协调者在告诉所有的分支都开始提交事务,直接根据本地事务id+xid删除对应的undo_log表记录即可。
笔记!
Seata实现原理
1.TM(发起方)连接到我们的TC事务协调者,创建一个全局的事务的xid,保存到ThreadLoacl中;
2.TM(发起方)和RM(参与方)都被Seata的数据数据源实现代理,在原生的sql之前和之后保存原来和修改后日志到undo_log中,方便后期实现回滚。
3.TM(发起方)使feign客户端调用接口时候,在ThreadLoacl中获取xid,设置到请求头中;
4.RM(参与方)从请求中获取到该xid,设置到ThreadLoacl中,同时也会向seataserver注册该分支事务。
5.TM(发起方)将当前本地事务的结果,告诉给协调者TC,协调者TC在通知所有的分支是否回滚。
6. TM(发起方)如果调用接口成功之后抛出异常的情况下,告诉给协调者TC,协调者TC在通知所有的分支根据根据全局的xid和分支事务的id 查询分支数据源的undo_log日志表逆向生成sql语句实现回滚,同时删除对应的undo_log日志
7. TM(发起方)如果调用接口成功之后没有抛出任何的异常,告诉给协调者TC,协调者TC在通知所有的分支根据根据全局的xid和分支事务的id 查询分支数据源的 删除对应的undo_log日志表
如何逆向实现sql语句:
insert 逆向 delete
delete逆向insert
update逆向肯定是update
前置镜像和后置镜像sql演示:
select * from orderId=1 得到state=0 ----前置镜像
update order set state(1) where orderId=1 原生sql语句
select * from orderId=1 得到state=1 ----后置镜像
回滚(记录到undo_log):
Update order set state(0) where orderId=1 ---还原