一、什么是分布式事务
事务的发起方和支参与方连接的数据库不同,分布式事务就是在业务逻辑中,保证不同数据库的数据一致性。
二、分布式事务的解决方案
- 单体项目多数据源 可以jta+ Atomikos
- 基于rabbitmq的形式解决 最终一致性的思想
- 基于rocketmq解决分布式事务 采用事务消息
- LCN采用lcn模式 假关闭连接 (目前已经被淘汰)
- Alibaba的Seata 背景非常强大 未来可能是主流
三、LCN
1、LCN基本实现原理
- 发起方与参与方都与我们的LCN管理器一直保持长连接;
- 发起方在调用接口之前,先向LCN管理器申请一个全局的事务分组id;
- 发起方调用接口的时候在请求头中传递事务分组id;
- 参与方获取到请求头中有事务分组的id的,则当前业务逻辑执行完实现假关闭,不会提交或者回滚当前的事务。
- 发起方调用完接口后,如果出现异常的情况,通知事务协调者回滚事务,这时候事务协调再告诉给参与方回滚当前事务。
2、LCN解决分布式事务的时候可能发生的问题?
LCN采用事务假关闭,如果因为网络原因导致事务没能及时提交或回滚,有可能一直开启行锁,其他线程访问该数据的时候,无法做写的操作。
3、LCN如何判断自己是发起方还是参与方?
参与方可以在请求头中获取到该全局事务id,如果能够获取到全局事务id的情况,则认为自己就是参与方,就会将该全局事务id缓存到ThreadLocal中。
四、Seata
1、Seata的组成结构
1)事务协调器(TC):维护全局事务和分支事务的状态,驱动全局提交或回滚,相当于是协调者。
2)事务管理器TM:定义全局事务的范围:开始全局事务,提交或回滚全局事务,相当于LCN中发起方。
3)资源管理器(RM):管理分支事务正在处理的资源,与TC进行对话以注册分支事务并报告分支事务的状态,并驱动分支事务的提交或回滚,相当于是LCN中的参与方
2、Seata解决分布式事务原理
订单(发起方)调用派单(参与方)服务接口:
- 需要构建全局事务协调者(TC)
- 订单服务TM(发起方)、派单RM(参与方)与我们全局事务协调者(TC)建立一个长连接;
- 订单服务TM(发起方)就会向全局事务协调者(TC)申请一个全局事务的xid,缓存到ThreadLocal中。
- 订单(发起方)调用派单(参与方)服务接口,使用到feign客户端技术,从ThreadLocal中获取刚才申请的全局事务id,放入到请求头中。
- 派单(参与方)可以在请求头中获取到该全局事务id,如果能够获取到全局事务id的情况,则认为自己就是参与方,就会将该全局事务id缓存到ThreadLocal中。
- 派单服务(参与方)开始记录一个undolog日志,回滚的日志。
-----原来状态state ==0;
-----后置镜像 state ==0;
-----参与方执行业务:Update set state(1) where id=1;
前置镜像 state ==1;
-----回滚至原来状态,修改状态:Update set state(0) where id=1;
update对应update回滚
Insert对应delete回滚
Delete对应insert回滚 - 如果认为自己是参与方的情况下,则将该信息注册到TC事务协调者中。
- 订单(发起方)调用完派单(参与方)接口之后,如果没有报错的情况下,就会通知给全局事务协调者,告诉另外的参与方不需要做回滚操作,需要删除自己记录的undolog日志。
-----原来状态state ==0;
-----参与方执行业务:Update set state(1) where id=1;
前置镜像 state ==1;
-----回滚至原来状态,修改状态:Update set state(0) where id=1;
-----后置镜像 state ==0;
3、Seata服务器端搭建
1)建表
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`lock_key` varchar(128) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint(4) DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(32) DEFAULT NULL,
`transaction_service_group` varchar(32) DEFAULT NULL,
`transaction_name` varchar(128) DEFAULT NULL,
`timeout` int(11) DEFAULT NULL,
`begin_time` bigint(20) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(96) DEFAULT NULL,
`transaction_id` mediumtext,
`branch_id` mediumtext,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(36) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2)修改配置文件file.conf
修改:file.conf store mode改为db
修改:registry.conf
3)Seata底层如何解决分布式事务
4、Seata 如何生成全局xid
Seata 发起方(TM)先向TC事务协调者申请一个全局的事务xid,事务发起方连接到tc事务协调者申请一个全局的xid,放入到threadlocal中缓存起来。
5、Seata如何生成前置与后置镜像
订单服务:seata代理数据源--
前置镜像---实际在执行addoRDER之前的数据 该数据是没有的。
int result = orderMapper.addOrder(newOrder);
后置镜像实际在执行addOrder结果 在数据库表中有一条记录
6、Seata如何传递xid的?
- 事务发起方连接到tc事务协调者申请一个全局的xid,放入到threadlocal中缓存起来
- 订单服务使用feign客户端技术 调用派单服务接口,在请求头中传递该xid。
- 派单服务从请求头中获取到该全局xid。
-
Aop代理----生成全局事务id放入到threadlocal
Insertorder接口---
继续feign调用接口 ---feign拦截器 从threadlocal中获取到全局的xid,放入到请求中 SeataFeignClient
以上三个步骤都是同一个线程
派单服务:
---拦截器 从请求头中获取到全局xid 获取到了全局xid,缓存到threadlocal
---aop类 判断方法上是否有加上事务注解 没有在生成一个新的全局xid。
----派单服务接口
7、Seata如何实现逆向回滚?
逆向回滚:直接将该条数据给删除。 是有谁来通知的?协调者
会将该前置和后置镜像插入到undolog日志表中。
Insert---逆向回滚 delete
原来状态0 Update(1) 逆向回滚 Update(0)
Delete 逆向回滚Insert
8、如果协调者发起方宕机了?参与方事务是回滚还是提交?
协调者会与发起方保持长连接,一旦发现发起方宕机,会通知参与方回滚,但发起方不能立即回滚,下次启动的时候会读取日志进行回滚。
9、如果协调者宕机?发起方没有通知给协调者?到底是提交还是回滚?
建议做集群、故障转移