分布式事务与 TX-LCN 使用

355 阅读8分钟

事务

事务的四个特性

  1. 原子性
      把整个事务看作一个不可再分 原子,也就是整个事务的操作要么全部执行,要么全部不执行。

  2. 一致性
      事务开始前和结束后,数据库原有的物理约束和逻辑约束保持一致。比如说一个方法需要同时往两个表中插入数据,第一次插入操作成功之后,第二次插入操作失败,这时候就不符合数据库的一致性,所以这时候就需要事务来进行回滚保证一致性。

  3. 隔离性
      多个事务并发执行时,一个事务的执行不影响其它事务的执行。

  4. 持久性
      一个事务一旦提交,对数据库的修改应该永久保存在数据库中。

隔离级别

  1. 读未提交
  2. 读已提交
  3. 可重复读
  4. 串行化

  MySQL 默认的隔离级别是可重复读,能有效避免脏读和不可重复读(虚读),但是有可能出现幻读

事务传播行为

  事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

  spring定义了七种传播行为,spring默认是第一种事务行为。

事务行为说明
PROPAGATION_REQUIRED支持当前事务,假设当前没有事务。就新建一个事务
PROPAGATION_SUPPORTS支持当前事务,假设当前没有事务,就以非事务方式运行
PROPAGATION_MANDATORY支持当前事务,假设当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW新建事务,假设当前存在事务。把当前事务挂起
PROPAGATION_NOT_SUPPORTED以非事务方式运行操作。假设当前存在事务,就把当前事务挂起
PROPAGATION_NEVER以非事务方式运行,假设当前存在事务,则抛出异常
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

分布式事务

  本地事务 @Transactional 只能保证一个 connection 的情况下,事务传播才有效,如果跨服务调用,不是一个 connection 的情况下,不然在遇到异常时,每个本地事务只能保证该 connection 执行的 sql 语句回滚,这时候就需要分布式事务。

CAP理论

  • 一致性(Consistency)即更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致
  • 可用性(Availability)即服务一直可用,而且是正常响应时间,最新的数据,任何时候任何应用程序都能读取写入数据
  • 分区容错性(Partition tolerance)系统部分节点出现故障后,连接正常节点还可以使用系统提供的服务

  分布式系统最多满足其中的两个特性,无法同时保持 CAP,分布式系统P是前提,必须保证,如果保证数据库 1 和数据库 2 的一致性,就必须同时锁定数据库 2 的读写操作,无法保证可用性

BASE理论

  • 基本可用(Basically Available)是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用

  • 软状态(Soft State)是状态可以有一段时间不同步,存在异步的情况,而该状态不会影响系统整体可用性。

  • 最终一致性(Eventually Consistent)是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态
    既是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)

柔性事务和刚性事务

  柔性事务满足 BASE 理论(基本可用,最终一致),刚性事务满足 ACID 理论

柔性事务分为

1)两阶段型
2)补偿型
3)异步确保型
4)最大努力通知型

分布式一致性协议

2PC

第一阶段:准备阶段(投票阶段)
第二阶段:提交阶段(执行阶段)

  阶段1-协调者发送一个提议,分别询问各参与者是否接受,这时候参与者开始执行事务操作,返回执行成功与否。

  阶段2-协调者根据参与者的反馈,提交或终止事务,提交事务成功,返回确认消息,协调方根据确认消息,决定本次事务是否成功。

2PC容易出现的问题

  • 协调者正常,参与者宕机,会陷入阻塞状态,解决方法:引入超时机制

  • 协调者宕机,参与者正常,特别在第二阶段,协调者无法向参与者发送提交请求,所有参与者都属于操作但是未提交的状态,会陷入阻塞情况

3PC

  是 2PC 的改进版本,一是引入超时机制,二是在第一阶段和第二阶段中间插入准备阶段

  那么引入了超时机制,参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这一阶段了大概率是提交的,如果是等待预提交命令超时,那该干啥就干啥了,反正本来啥也没干。****

  所以说 3PC 就是通过引入预提交阶段来使得参与者之间的状态得到统一,也就是留了一个阶段让大家同步一下。

  但是超时机制可能造成数据不一致

1、CanCommit阶段

分为两步:

  1. 事务询问:协调者向参与者发送CanCommit请求,寻问是否可以执行事务提交操作,然后开始等待参与者的响应
  2. 响应反馈: 参与者接收到CanCommit请求后,根据自身情况返回Yes或No

2.PreCommit阶段

就跟2PC的第一阶段差不多,执行操作,但是不提交事务,然后返回信息给协调者,协调者和参与者都引入了超时机制

3.DoCommit阶段

和2PC的阶段二差不多

TX-LCN使用

1. 使用Tx-Manager、单独的项目

1.1 依赖jar

<dependency>
    <groupId>com.codingapi.txlcn</groupId>
    <artifactId>txlcn-tm</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

1.2 配置文件properties

spring.application.name=TransactionManager
server.port=7970
# JDBC 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/user?characterEncoding=UTF-8&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=654321

# 数据库方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect

# 为TM创建持久化数据库表
spring.jpa.hibernate.ddl-auto=update

# TM监听Socket端口. 默认为 ${server.port} - 100
tx-lcn.manager.port=8070
# TM后台登陆密码,默认值为codingapi
tx-lcn.manager.admin-key=qfjava
# 雪花算法的sequence位长度,默认为12位.
tx-lcn.manager.seq-len=12
# 异常回调开关。开启时请制定ex-url
tx-lcn.manager.ex-url-enabled=false
# 开启日志,默认为false
tx-lcn.logger.enabled=true
tx-lcn.logger.driver-class-name=${spring.datasource.driver-class-name}
tx-lcn.logger.jdbc-url=${spring.datasource.url}
tx-lcn.logger.username=${spring.datasource.username}
tx-lcn.logger.password=${spring.datasource.password}

# redis 的设置信息. 线上请用Redis Cluster
spring.redis.host=118.31.224.147
spring.redis.port=6379
spring.redis.password=154050

1.3 开关类上使用注解:

@EnableTransactionManagerServer //启用Tx-Manager 事务管理器

1.4 创建数据库

CREATE TABLE `t_tx_exception`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `transaction_state` tinyint(4) NULL DEFAULT NULL,
  `registrar` tinyint(4) NULL DEFAULT NULL,
  `remark` varchar(4096) NULL DEFAULT  NULL,
  `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

1.5 运行访问

http://localhost:8801/admin/index.html

2.应用分布式事务:Tx-LCN在服务中使用

2.1 依赖jar

<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.2 开关类添加注解

  各个服务的开关上使用注解 @EnableDistributedTransaction 开启分布式事务

2.3 在要进行分布式事务的方法上使用以下注解,要带上本地注解@Transaction,使用的 注解要固定的一个,否则参数为null

@LcnTransaction //lcn模式
	LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地	 	 事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
@TxcTransaction //txc模式
	TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建	锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。
@TccTransaction //tcc模式
    TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对	XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行	  业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。

例子

    @LcnTransaction //lcn模式
//    @TccTransaction //txc模式
//    @TxcTransaction //tcc模式
    @Transactional(rollbackFor = Exception.class)
    public void insert(Test1 test1) {
        try {
//            开启事务
        testDao.insert(test1);
        serviceTest.insert(test1);
        System.out.println(1 / 0);
//            提交事务
        } catch (Exception e) {
            System.out.println("出现了异常");
            DTXUserControls.rollbackCurrentGroup();
        }
    }

2.4 修改配置文件

添加tx-lcn管理器的地址
tx-lcn:
  client:
  	manager-address: localhost:8070

分布式事务tx-lcn下lcn模式时spring手动事务回滚失效

1.改用 @TccTransaction来替换lcn模式

2.使用DTXUserControls.rollbackCurrentGroup();来替换TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();