java分布式事务~go

1、分布式事务介绍

(1)什么是事务?
举个生活中的例子:你去小卖铺买东西,“一手交钱,一手交货”就是一个事务的例子,交钱和交货必须全部成功,事务才算成功,任一个活动失败,事务将撤销所有已成功的活动。
明白上述例子,再来看事务的定义:
事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。
(2)什么是本地事务?
在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据库事务,由于应用主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务。回顾一下数据库事务的四大特性 ACID:

  • A(Atomic):原子性,构成事务的所有操作,要么都执行完成,要么全部不执行,不可能出现部分成功部分失败的情况。
  • C(Consistency):一致性,在事务执行前后,数据库的一致性约束没有被破坏。比如:张三向李四转100元,转账前和转账后的数据是正确状态这叫一致性,如果出现张三转出100元,李四账户没有增加100元这就出现了数据错误,就没有达到一致性。
  • I(Isolation):隔离性,数据库中的事务一般都是并发的,隔离性是指并发的两个事务的执行互不干扰,一个事务不能看到其他事务运行过程的中间状态。通过配置事务隔离级别可以避脏读、重复读等问题。
  • D(Durability):持久性,事务完成之后,该事务对数据的更改会被持久化到数据库,且不会被回滚。 数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚

分布式事务
随着互联网的快速发展,软件系统由原来的单体应用转变为分布式应用
在这里插入图片描述分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分事务、创建订单减库存事务,银行转账事务等都是分布式事务。

我们知道本地事务依赖数据库本身提供的事务特性来实现,因此以下逻辑可以控制本地事务:

begin transation;
// 1.本地数据库操作:张三减少金额
// 2.本地数据库操作:李四增加金额
commit transation;

但是在分布式环境下,会变成下边这样:

begin transation;
// 1.本地数据库操作:张三减少金额
// 2.远程调用:李四增加金额
commit transation;

可以设想,当远程调用让李四增加金额成功了,由于网络问题远程调用并没有返回,此时本地事务提交失败就回滚了张三减少金额的操作,此时张三和李四的数据就不一致了。

因此在分布式架构的基础上,传统数据库事务就无法使用了,张三和李四的账户不在一个数据库中甚至不在一个应用系统里,实现转账事务需要通过远程调用,由于网络问题就会导致分布式事务问题。

2、分布式事务使用场景

(1)典型的场景就是微服务架构 微服务之间通过远程调用完成事务操作。 比如:订单微服务和库存微服务,下单的同时订单微服务请求库存微服务减库存。 简言之:跨JVM进程产生分布式事务。
在这里插入图片描述
(2)单体系统访问多个数据库实例 当单体系统需要访问多个数据库(实例)时就会产生分布式事务。 比如:用户信息和订单信息分别在两个MySQL实例存储,用户管理系统删除用户信息,需要分别删除用户信息及用户的订单信息,由于数据分布在不同的数据实例,需要通过不同的数据库链接去操作数据,此时产生分布式事务。 简言之:跨数据库实例产生分布式事务。
在这里插入图片描述
(3)多服务访问同一个数据库实例 比如:订单微服务和库存微服务即使访问同一个数据库也会产生分布式事务,原因就是跨JVM进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务
在这里插入图片描述

3、分布式相关概念

(1)cap概念
CAP是Consistency、Availability、Partition tolerance三个词语的缩写,分别表示一致性、可用性、分区容忍性。
解释:
为了方便对CAP理论的理解,我们结合电商系统中的一些业务场景来理解CAP。
如下图,是商品信息管理的执行流程:
在这里插入图片描述
在这里插入图片描述
C-Consistency
在这里插入图片描述

A-Availability
在这里插入图片描述

P-Partition tolerance
在这里插入图片描述

注意:

  • C是不可以响应旧数据,而A是可以的

所有的分布式系统不能同时具备CAP功能,P是都具有的,但A和C是互斥不能同时具有的
在这里插入图片描述
在这里插入图片描述

eg:

组合方式:
在这里插入图片描述

上面的商品管理系统若实现了CA则如下图
在这里插入图片描述

主数据库和从数据库中间不再进行数据同步,数据库可以响应每次的查询请求,通过事务隔离级别实现每个查询请求都可以返回最新的数据
在这里插入图片描述(2)base理论
(i)强一致性与最终一致性
在这里插入图片描述(ii)BASE介绍
在这里插入图片描述

4、分布式事务解决方案

4.1、 2pc

(1)2PC介绍
2pc即两阶段提交协议,是将整个事务流程分为两阶段,分别为准备阶段(Prepare phase),提交阶段(commit phase),2是值两阶段,P是指准备阶段,C是指提交阶段。
eg:
在这里插入图片描述![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ab638698895d428e9fee857c134112af.png
2pc过程图如下
A:成功
在这里插入图片描述
B:失败
在这里插入图片描述2PC缺点?

  • 同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。(1pc准备阶段,只执行sql,而不提交,并且占用数据库连接资源)
  • 单点故障。由于协调者的的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
  • 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这导致只有一部分参与者接到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
  • 二阶段无法解决的问题:协调者在发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

7.2、2pc两阶段解决方案策略
(1)XA方案
2PC的传统方案是在数据库层面实现的,如Oracle、MySQL都支持2PC协议,为了统一标准减少行业内不必要的对接成本,需要制定标准化的处理模型及接口标准,国际开放标准组织Open Group定义了分布式事务处理模型DTP(Distributed Transaction Processing Reference Model)。
以下例子是使用了XA方案
在这里插入图片描述

执行流程:

  • 应用程序(AP)持有用户库和积分库两个数据源。
  • 应用程序(AP)通过TM通知用户库RM新增用户,同时通知积分库RM为该用户新增积分,RM此时并未提交事务,此时用户和积分资源锁定。
  • TM收到执行回复,只要有一方失败则分别向其他RM发起回滚事务,回滚完毕,资源锁释放。
  • TM收到执行回复,全部成功,此时向所有RM发起提交事务,提交完毕,资源锁释放。

DTP定义以下角色
在这里插入图片描述
XA方案存在问题
需要本地数据库支持XA协议
资源锁需要等到两个阶段结束才释放,性能较差

(2)Seata方案
Seata是由阿里中间件团队发起的开源项目Fescar,后更名为Seata,它是一个是开源的分布式事务框架。
传统2PC的问题在Seata中得到解决,它通过对本地关系数据库的分支事务的协调来驱动完成全局事务,是工作在应用层的中间件。主要优点是性能较好,且不长时间占用连接资源,它以高效并且对业务0侵入的方式解决微服务场景下面临的分布式事务问题,它目前提供AT模式(即2PC)、TCC模式的分布式事务解决方案。
Seata的设计思想如下:
Seata的设计目标其一是对业务无入侵,因此从业务无入侵的2PC方案着手,在传统2PC的基础上演进,并解决2PC方案面临的问题。
Seata把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是个关系数据库的本地事务,下图是全局事务与分支事务的关系图:
在这里插入图片描述

与传统的2pc的模型类似,Seata定义了3个组件来协议分布式事务的处理过程:
在这里插入图片描述在这里插入图片描述

eg
注册积分例子
在这里插入图片描述在这里插入图片描述
Seata与传统的2pc区别:
在这里插入图片描述
seata的流程
在这里插入图片描述
Seata安裝
下载地址

https://github.com/seata/seata/releases/download/v0.7.1/seata-server-0.7.1.zip

解压文件
到bin启动
命令

seata-server.bat -p 8888 -m file

出现以下字样。代表成功启动
在这里插入图片描述
注意: 其中8888为服务端口号;file为启动模式,这里指seata服务将采用文件的方式存储信息。
(4)需要在数据路新增一张undo_log表

create table undo_log

(
    id            bigint auto_increment primary key,
    branch_id     bigint       not null,
    xid           varchar(100) not null,
    context       varchar(128) not null,
    rollback_info longblob     not null,
    log_status    int          not null,
    log_created   datetime     not null,
    log_modified  datetime     not null,
    ext           varchar(100) null,
    constraint ux_undo_log
        unique (xid, branch_id)
);

seata的配置
(1)将配置文件拷到resource目录下
在这里插入图片描述

(2)修改配置文件
修改部分
在这里插入图片描述

vgroup_mapping.事务分组服务名=Seata Server集群名称(默认名称为default)
default.grouplist = Seata Server集群地址
在 org.springframework.cloud:spring-cloud-starter-alibaba-seata 的
org.springframework.cloud.alibaba.seata.GlobalTransactionAutoConfiguration 类中,默认会使用
${spring.application.name}-fescar-service-group 作为事务分组服务名注册到 Seata Server上,如果和
file.conf 中的配置不一致,会提示 no available server to connect 错误
也可以通过配置 spring.cloud.alibaba.seata.tx-service-group 修改后缀,但是必须和file.conf 中的配置保持
一致。
注意:
在这里插入图片描述

(3)数据源配置
在这里插入图片描述在这里插入图片描述

(3)小结
在这里插入图片描述

4.2、TCC

(1)TCC介绍
TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认
Confirm、撤销Cancel。Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的
操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所
有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel
操作若执行失败,TM会进行重试。
在这里插入图片描述

TCC分为三个阶段:

  1. Try 阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm 一起才能真正构成一个完整的业务逻辑。

  2. Confirm 阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则
    认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引
    入重试机制或人工处理。

  3. Cancel 阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采
    用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理。

  4. TM事务管理器
    TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了成为公 用组件,是为了考虑系统结构和软件复用。
    TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务上下文,
    追踪和记录状态,由于Confirm 和cancel失败需进行重试,因此需要实现为幂等,幂等性是指同一个操作无论请求
    多少次,其结果都相同。
    tcc分布式解决方案
    在这里插入图片描述
    Seata的TCC模式对Spring Cloud并没有提供支持。

hmily解说
Hmily是一个高性能分布式事务TCC开源框架。基于Java语言来开发(JDK1.8),支持Dubbo,Spring Cloud等
RPC框架进行分布式事务。它目前支持以下特性:

  • 支持嵌套事务(Nested transaction support).
  • 采用disruptor框架进行事务日志的异步读写,与RPC框架的性能毫无差别。
  • 支持SpringBoot-starter 项目启动,使用简单。
  • RPC框架支持 : dubbo,motan,springcloud。
  • 本地事务存储支持 : redis,mongodb,zookeeper,file,mysql。
  • 事务日志序列化支持 :java,hessian,kryo,protostuff。
  • 采用Aspect AOP 切面思想与Spring无缝集成,天然支持集群。
  • RPC事务恢复,超时异常恢复等。

Hmily利用AOP对参与分布式事务的本地方法与远程方法进行拦截处理,通过多方拦截,事务参与者能透明的
调用到另一方的Try、Confirm、Cancel方法;传递事务上下文;并记录事务日志,酌情进行补偿,重试等。

Hmily不需要事务协调服务,但需要提供一个数据库(mysql/mongodb/zookeeper/redis/file)来进行日志存
储。

Hmily实现的TCC服务与普通的服务一样,只需要暴露一个接口,也就是它的Try业务。Confirm/Cancel业务
逻辑,只是因为全局事务提交/回滚的需要才提供的,因此Confirm/Cancel业务只需要被Hmily TCC事务框架
发现即可,不需要被调用它的其他业务服务所感知。

官网介绍:

https://dromara.org/website/zh-cn/docs/hmily/index.html

TCC需要注意三种异常处理分别是空回滚、幂等、悬挂:

  • 空回滚:

在没有调用 TCC 资源 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回
滚,然后直接返回成功。
出现原因是当一个分支事务所在服务宕机或网络异常,分支事务调用记录为失败,这个时候其实是没有执行Try阶
段,当故障恢复后,分布式事务进行回滚则会调用二阶段的Cancel方法,从而形成空回滚。
解决思路是关键就是要识别出这个空回滚。思路很简单就是需要知道一阶段是否执行,如果执行了,那就是正常回
滚;如果没执行,那就是空回滚。前面已经说过TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分
布式事务调用链条。再额外增加一张分支事务记录表,其中有全局事务 ID 和分支事务 ID,第一阶段 Try 方法里会
插入一条记录,表示一阶段执行了。Cancel 接口里读取该记录,如果该记录存在,则正常回滚;如果该记录不存
在,则是空回滚。

  • 幂等:

通过前面介绍已经了解到,为了保证TCC二阶段提交重试机制不会引发数据不一致,要求 TCC 的二阶段 Try、
Confirm 和 Cancel 接口保证幂等,这样不会重复使用或者释放资源。如果幂等控制没有做好,很有可能导致数据
不一致等严重问题。
解决思路在上述“分支事务记录”中增加执行状态,每次执行前都查询该状态。

  • 悬挂:

悬挂就是对于一个分布式事务,其二阶段 Cancel 接口比 Try 接口先执行。
出现原因是在 RPC 调用分支事务try时,先注册分支事务,再执行RPC调用,如果此时 RPC 调用的网络发生拥堵,
通常 RPC 调用是有超时时间的,RPC 超时以后,TM就会通知RM回滚该分布式事务,可能回滚完成后,RPC 请求
才到达参与者真正执行,而一个 Try 方法预留的业务资源,只有该分布式事务才能使用,该分布式事务第一阶段预
留的业务资源就再也没有人能够处理了,对于这种情况,我们就称为悬挂,即业务资源预留后没法继续处理。
解决思路是如果二阶段执行完成,那一阶段就不能再继续执行。在执行一阶段事务时判断在该全局事务下,“分支
事务记录”表中是否已经有二阶段事务记录,如果有则不执行Try。

hmily安装
springboot有提供 hmily整合的包
步骤:
(1)引入包【注意jar包版本需要用2.0.4-RELEASE,其他版本jar报不全】

<dependency>
   <groupId>org.dromara</groupId>
   <artifactId>hmily-springcloud</artifactId>
   <version>2.0.4-RELEASE</version>
</dependency>

(2)创建 hmily库和记录日志表

CREATE TABLE `local_try_log`
(
    `tx_no`       varchar(64) NOT NULL COMMENT '事务id',
    `create_time` datetime DEFAULT NULL,
    PRIMARY KEY (`tx_no`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;


CREATE TABLE `local_confirm_log`
(
    `tx_no`       varchar(64) NOT NULL COMMENT '事务id',
    `create_time` datetime DEFAULT NULL
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;


CREATE TABLE `local_cancel_log`
(
    `tx_no`       varchar(64) NOT NULL COMMENT '事务id',
    `create_time` datetime DEFAULT NULL,
    PRIMARY KEY (`tx_no`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;


CREATE DATABASE `hmily` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

(3)编写配置类

package cn.itcast.dtx.tccdemo.bank2.config;


import com.alibaba.druid.pool.DruidDataSource;
import org.dromara.hmily.common.config.HmilyDbConfig;
import org.dromara.hmily.core.bootstrap.HmilyTransactionBootstrap;
import org.dromara.hmily.core.service.HmilyInitService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.env.Environment;


@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class DatabaseConfiguration {


@Autowired
private Environment env;


private final ApplicationContext applicationContext;


public DatabaseConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;

}




@Bean
   @ConfigurationProperties(prefix = "spring.datasource.ds1")  //指定数据源
public DruidDataSource ds1() {

DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;

}


@Bean //获取配置文件中的配置
public HmilyTransactionBootstrap hmilyTransactionBootstrap(HmilyInitService hmilyInitService){

HmilyTransactionBootstrap hmilyTransactionBootstrap = new HmilyTransactionBootstrap(hmilyInitService);

hmilyTransactionBootstrap.setSerializer(env.getProperty("org.dromara.hmily.serializer"));

hmilyTransactionBootstrap.setRecoverDelayTime(Integer.parseInt(env.getProperty("org.dromara.hmily.recoverDelayTime")));

hmilyTransactionBootstrap.setRetryMax(Integer.parseInt(env.getProperty("org.dromara.hmily.retryMax")));

hmilyTransactionBootstrap.setScheduledDelay(Integer.parseInt(env.getProperty("org.dromara.hmily.scheduledDelay")));

hmilyTransactionBootstrap.setScheduledThreadMax(Integer.parseInt(env.getProperty("org.dromara.hmily.scheduledThreadMax")));

hmilyTransactionBootstrap.setRepositorySupport(env.getProperty("org.dromara.hmily.repositorySupport"));

hmilyTransactionBootstrap.setStarted(Boolean.parseBoolean(env.getProperty("org.dromara.hmily.started")));

HmilyDbConfig hmilyDbConfig = new HmilyDbConfig();

hmilyDbConfig.setDriverClassName(env.getProperty("org.dromara.hmily.hmilyDbConfig.driverClassName"));

hmilyDbConfig.setUrl(env.getProperty("org.dromara.hmily.hmilyDbConfig.url"));

hmilyDbConfig.setUsername(env.getProperty("org.dromara.hmily.hmilyDbConfig.username"));

hmilyDbConfig.setPassword(env.getProperty("org.dromara.hmily.hmilyDbConfig.password"));

hmilyTransactionBootstrap.setHmilyDbConfig(hmilyDbConfig);
return hmilyTransactionBootstrap;

}

}

(4)添加配置

org:
  dromara:
    hmily :
      serializer : kryo
      recoverDelayTime : 30
      retryMax : 30
      scheduledDelay : 30
      scheduledThreadMax :  10
      repositorySupport : db
      started: false
      hmilyDbConfig :
        driverClassName : com.mysql.jdbc.Driver
        url :  jdbc:mysql://localhost:3306/hmily?useUnicode=true
        username : root
        password : 4009mjj

(5)对需要处理事务的接口标注以下注解【confirmMethod:confirm 方法,cancelMethod:回滚方法】

@Hmily(confirmMethod="commit",cancelMethod="rollback")

(6)编写confirmMethod和cancelMethod方法
(i) mq保证消息一致性解决分布式事务问题
概念
在这里插入图片描述

rocketmq解决方案
在这里插入图片描述

使用步骤:
(1)安装rocketmq
下载地址:

http://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/4.5.0/rocketmq-all-4.5.0-bin-release.zip

解压启动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值