分布式事务解决方案

一、什么是分布式事务

        事务的发起方和支参与方连接的数据库不同,分布式事务就是在业务逻辑中,保证不同数据库的数据一致性。

二、分布式事务的解决方案

  1. 单体项目多数据源 可以jta+ Atomikos
  2. 基于rabbitmq的形式解决 最终一致性的思想
  3. 基于rocketmq解决分布式事务 采用事务消息
  4. LCN采用lcn模式 假关闭连接 (目前已经被淘汰)
  5. Alibaba的Seata  背景非常强大 未来可能是主流

三、LCN

1、LCN基本实现原理

  1. 发起方与参与方都与我们的LCN管理器一直保持长连接;
  2. 发起方在调用接口之前,先向LCN管理器申请一个全局的事务分组id;
  3. 发起方调用接口的时候在请求头中传递事务分组id;
  4. 参与方获取到请求头中有事务分组的id的,则当前业务逻辑执行完实现假关闭,不会提交或者回滚当前的事务。
  5. 发起方调用完接口后,如果出现异常的情况,通知事务协调者回滚事务,这时候事务协调再告诉给参与方回滚当前事务。

2、LCN解决分布式事务的时候可能发生的问题?

        LCN采用事务假关闭,如果因为网络原因导致事务没能及时提交或回滚,有可能一直开启行锁,其他线程访问该数据的时候,无法做写的操作。

3、LCN如何判断自己是发起方还是参与方?

       参与方可以在请求头中获取到该全局事务id,如果能够获取到全局事务id的情况,则认为自己就是参与方,就会将该全局事务id缓存到ThreadLocal中。

四、Seata

1、Seata的组成结构

1)事务协调器(TC):维护全局事务和分支事务的状态,驱动全局提交或回滚,相当于是协调者。

2)事务管理器TM:定义全局事务的范围:开始全局事务,提交或回滚全局事务,相当于LCN中发起方

3)资源管理器(RM):管理分支事务正在处理的资源,与TC进行对话以注册分支事务并报告分支事务的状态,并驱动分支事务的提交或回滚,相当于是LCN中的参与方

2、Seata解决分布式事务原理

订单(发起方)调用派单(参与方)服务接口:

  1. 需要构建全局事务协调者(TC)
  2. 订单服务TM(发起方)、派单RM(参与方)与我们全局事务协调者(TC)建立一个长连接;
  3. 订单服务TM(发起方)就会向全局事务协调者(TC)申请一个全局事务的xid,缓存到ThreadLocal中。
  4. 订单(发起方)调用派单(参与方)服务接口,使用到feign客户端技术,从ThreadLocal中获取刚才申请的全局事务id,放入到请求头中。
  5. 派单(参与方)可以在请求头中获取到该全局事务id,如果能够获取到全局事务id的情况,则认为自己就是参与方,就会将该全局事务id缓存到ThreadLocal中。
  6. 派单服务(参与方)开始记录一个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回滚
  7. 如果认为自己是参与方的情况下,则将该信息注册到TC事务协调者中。
  8. 订单(发起方)调用完派单(参与方)接口之后,如果没有报错的情况下,就会通知给全局事务协调者,告诉另外的参与方不需要做回滚操作,需要删除自己记录的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的?

  1. 事务发起方连接到tc事务协调者申请一个全局的xid,放入到threadlocal中缓存起来
  2. 订单服务使用feign客户端技术 调用派单服务接口,在请求头中传递该xid。
  3. 派单服务从请求头中获取到该全局xid。
  4. 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、如果协调者宕机?发起方没有通知给协调者?到底是提交还是回滚?

        建议做集群、故障转移

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值