分布式事务Seata、LCN的原理深度剖析

一、基本概念和方法

1.1、分布式事务产生背景

(1)如果是在传统项目中,使用同一个数据源,在数据用同一个事务管理器的情况下,不存在分布式事务问题,因为有事务的传播行为帮助我们实现。每个数据源都有自己独立的事务管理,每个数据源中的事务管理都互不影响。

(2)如果在单体项目中,存在多个不同的数据源,每个事务源都有自己独立的事务管理器,每个事务管理器互不影响,也会存在分布式事务的问题。

1.2、Rpc通讯中产生的分布式事务的问题原因

(1)调用放调用完rpc接口后,突然程序抛出异常,调用放的事务回滚了,但是被调用方接口没有回滚,则会产生数据不一致问题。在每个jvm中都有自己的事务,每个事务都互不影响。

(2)被调用方的接口失败的话,调用方可以根据返回的结果,手动回滚调用方本地事务。

1.3、分布式事务解决框架有哪些?

(1)传统多数据源的情况下,采用jta + atominc将每个独立的事务管理器统一交给我们的atominc全局事务管理。

(2)基于MQ保证数据一致性,最终一致性。

(3)基于Rocketmq解决分布式事务,核心采用自带事务消息。

(4)基于LCN模式解决分布式事务,tcc/2pc/lcn模式。全局事务id来回传递

(5)基于阿里巴巴seata解决分布式事务(背景非常强大)。

分布式事务处理的核心思想:tcc、2pc、最终一致性。

**1.4、**CAP和Base理论

这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

一致性(C):

在分布式系统中的所有数据备份,在同一时刻是否同样的值,等同于所有节点访问同一份最新的数据副本。即:在分布式系统中,同一时刻所有的节点的数据都是相同的。

可用性(A):

在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求,对数据更新具备高可用性。即:集群中部分节点出现了故障,集群的整体也能够给响应

分区容错性(P):

以实际效果而言,分区相当于对通信的时限要求,系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。即:分区容错性是指系统能够容忍节点之间的网络通信的故障,意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

CAP总结:

三者无法兼顾,在分布式系统当中可以容忍网络之间出现的通讯故障,最终选择要么是CP或者AP。

**CP:**当你网络出现故障之后,只能保证数据一致性,但是不能保证可用性; 如zookeeper;

**AP:**当你网络出现故障之后,不能保证数据一致性,但是能够保证可用性;如eureka;

Base理论

BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写,即:是由基本可用、软状态、最终一致性三个组成,是对cap中一致性和可用性权衡的结果。

1.5、Seata与LCN的区别

1、基本实现的思路是一样的,唯一区别在于回滚的方式 2、LCN采用代理数据源假关闭连接,暂时不提交本地事务,但是容易造成数据的死锁。

Seata采用undo_log的形式逆向生成sql语句实现回滚,避免死锁现象但是容易出现脏读。

二、LCN解决分布式事务

2.1、基于LCN解决分布式事务原理

1、发起方和参与方项目启动的时候,必须和全局的协调者保持长连接。

2、发起方向我们的lcn的协调者申请一个事务分组id。

3、发起方调用参与方接口的时候,在请求头中将该事务的分组id传递给参与方。

4、参与方获取到请求头中有传递对应的事务分组id,当前业务执行完毕后,不会提交事务,基于数据源做的一个代理,采用jdbc假关闭,做了一行锁,容易锁行数据,

5、发起方如果产生回滚或提交,都会将该结果发送给协调者,然后协调者通知所有的参与方。

如果参与方一直没有等到协调者来告知的话,协调者端默认超时时间是5秒,超时之后就会做回滚操作。

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

根据当前的线程threadlocal中获取事务分组id,如果能够成功获取到,则为事务的参与方,如果获取不到则为发起方。

2.2、LCN整个实现过程

(1)判断方法是否有加上@LcnTransaction注解,如果有,则直接会走TransactionAspect来做切面拦截。

(2)当前线程缓存中是否有事务分组id,如果没有缓存,则视为发起方,如果有缓存,则视为参与方。

(3)随机的创建分组的id,将该分组id注册到协调者中。

(4)本地threadlocal缓存该事务分组id;

(5)通过feign调用接口时,底层重写RequestInterceptorFeign客户端,从threadlocal中获取该事务分组id,并设置到请求中。

(6)参与方在aop中通过SpringTracingApplier实现,在请求之前拦截,从请求头中获取事务分组id,放入到缓存中。

(7)从缓存中获取该事务分组id,当前服务则为参与方,再告诉给协调者加入该事务分组。

2.3、SpringCloud整合LCN解决分布式事务问题

2.3.1、引入相关pom依赖:

复制代码
<dependency>
    <groupId>com.codingapi.txlcn</groupId>
    <artifactId>txlcn-tc</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.codingapi.txlcn</groupId>
    <artifactId>txlcn-txmsg-netty</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

2.3.2、调用方和参与方添加以下配置,并在启动类上添加@EnableDistributedTransaction注解。

复制代码
tx-lcn: 
  client: 
    manager-address:127.0.0.1:8070
  logger: 
    enabled: true

2.3.3、在发起方和调用方的接口实现上都需添加@Transactional 和 @LcnTransaction注解即可。

三、Seata解决分布式事务原理及过程

3.1、Seata的组成部分

(1)事务协调器(TC):

维护全局事务和分支事务的状态,驱动全局提交或事务回滚,类似LCN的协调者。

(2)事务管理器(TM):

定义全局事务的范围;开始全局事务,提交或回滚全局事务,类似LCN的发起方。

(3)资源管理器(RM):

管理分支事务正在处理的资源,与TC进行对话,来注册分支事务,并报告分支事务的状态,并驱动分支事务的提交和回滚,相当于LCN的参与方。

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

(1)发起方(TM)和参与方(RM)再项目启动的时候,和协调者(TC)保持长连接。

(2)发起方调用接口之前向TC获取一个全局的事务id为xid,注册到Seata中,AOP中实现的。

(3)使用feign客户端调用接口的时候,seata重写了feign客户端,在请求头中传递该xid。

(4)参与方从请求头中获取该xid,方法执行完毕不会立马提交,而是等待协调者通知提交的状态。

3.3、Seata实现过程

  1. 发起方(TM)向协调者(TC)申请一个全局的事务xid,保存到ThreadLocal中;
  2. 发起方(TM)和参与方(RM)都会被Seata数据源代理,在原生的sql执行之前和之后,

把操作前和操作后的内容保存到undo_log表中(前置镜像和后置镜像),方便后期实现回滚。

  1. 发起方(TM)使用feign客户端调用接口的时候,在threadlocal中获取xid,设置到请求头中。
  2. 参与方(RM)从请求中头中获取到该全局事务xid,设置到threadlocal中,并且向协调者注册该分支。
  3. 如果发起方(TM)调用接口成功之后,如果报错的情况下则通知给协调者,协调者再告诉所有的分支都开始回滚,

直接根据本地事务id+xid查询undo_log表 ,逆向生成sql语句回滚,同时删除该undo_log日志。

  1. 如果发起方(TM)调用接口成功之后,如果没有报错的情况下则通知给协调者,协调者在告诉所有的分支都开始提交事务,直接根据本地事务id+xid删除对应的undo_log表记录即可。

**缺陷:**根据undolog表,逆向回滚,会产生脏读问题,在回滚或网络阻塞的情况下,会导致接口也超时和阻塞。

3.4、JAVA项目整合Seata解决分布式事务示例

3.4.1、服务端整合

(1)启动seata-server 全局事务协调者 seata-server.bat;

(2)修改 registry.conf和file.conf配置;

(3)在需要解决分布式事务的数据库中,手动创建undo_log表;

3.4.2、客户端整合(对应的方法上添加@GlobalTranscational注解)

(1)需要要将file.conf registry.conf 拷贝的项目中,默认的my_test_tx_group为分组的id、 全局的协调者连接地址。

复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-seata</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>

(2)配置代理的数据源

复制代码
@Configuration
  public class DataSourceProxyConfig {
  
      @Bean
      @ConfigurationProperties(prefix = spring.datasource)
      public DataSource dataSource() {
          return new DruidDataSource();
      }
  
      @Bean
      public DataSourceProxy dataSourceProxy(DataSource
  dataSource) {
          return new DataSourceProxy(dataSource);
      }
  
      @Bean
      public SqlSessionFactory
  sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
          SqlSessionFactoryBean
  sqlSessionFactoryBean = new SqlSessionFactoryBean();
         
  sqlSessionFactoryBean.setDataSource(dataSourceProxy);
         
  sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
          return sqlSessionFactoryBean.getObject();
      }
  }

(3)修改application.yml的配置:

四、手写Seata解决分布式事务设计思想

发起方在方法上面加上@GlobalTransactional,会被aop拦截;

Aop的入口分析:

1、我们的项目中会引入到SpringCloud-Alibaba-seata.jar 执行spring.factories配置,读取GlobalTransactionAutoConfiguration配置类,会加载GlobalTransactionScanner全局事务扫描器到Spring的容器中。

2、GlobalTransactionScanner实现了AbstractAutoProxyCreator、InitializingBean,

a)AbstractAutoProxyCreator:是SpringAOP原生类,帮助我们创建代理对象;

b)InitializingBean SpringBean生命周期初始化;

GlobalTransactionScanner 对象初始化成功之后开始注册我们的tm和tc,

AbstractAutoProxyCreator 回调的方法 wrapIfNecessary 来创建我们的GlobalTransactionalInterceptor,

执行我们发起方方法前会执行我们的GlobalTransactionalInterceptor的invoke方法 ,然后再执行我们的transactionalTemplate的execute方法。

相关推荐
尚学教辅学习资料1 小时前
SpringBoot3.x入门到精通系列:4.3 性能优化技巧
spring boot·性能优化
麦兜*4 小时前
Spring Boot集成方案 + Elasticsearch向量检索,语义搜索核弹
java·spring boot·python·spring·elasticsearch·spring cloud·系统架构
Absinthe_苦艾酒5 小时前
JVM学习专题(四)对象创建过程
java·jvm·后端
六毛的毛6 小时前
FastAPI入门:中间件、CORS跨域资源共享、SQL数据库
数据库·中间件·fastapi
hweiyu006 小时前
IDEA搭建GO环境
开发语言·后端·golang·intellij-idea·idea·intellij idea
Real_man6 小时前
RAG系统全景:架构详解与落地实践指南
后端
yh云想6 小时前
《RedisTemplate 核心操作全解析》
redis·spring·redistemplate
Wgllss6 小时前
完整案例:Kotlin+Compose+Multiplatform之桌面端音乐播放器,数据库使用实现(三)
android·架构·android jetpack
若梦plus6 小时前
Xata低代码服务器端数据库平台之技术分析
前端·后端