Java分布式事务及seata框架的使用

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 什么是事务?事务从本质上讲就是:逻辑上的一组操作,组成这组操作的各个逻辑单元在不同的服务甚至服务器上,保证它们要成功就都成功,要失败就都失败。事务的四大特性提到事务就不得不提事务的四大特性(基本特征) ACID:原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的

什么是事务?

事务从本质上讲就是:逻辑上的一组操作,组成这组操作的各个逻辑单元在不同的服务甚至服务器上,保证它们要成功就都成功,要失败就都失败。

事务的四大特性

提到事务就不得不提事务的四大特性(基本特征) ACID:

  • 原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
  • 一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。
  • 隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰
  • 持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。

并发事务可能会带来的问题

  • 脏读:一个事务可以读取另一个事务未提交的数据
  • 不可重复读:一个事务可以读取另一个事务已提交的数据 单条记录前后不匹配
  • 虚读(幻读):一个事务可以读取另一个事务已提交的数据 读取的数据前后多了或者少了

我们实践出真知:举个例子,mysql(5.6.16)

首先创建一张表:

CREATE TABLE `test_account` (
 `id` int(11) NOT NULL,
 `name` varchar(255) DEFAULT NULL,
 `money` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test_account` VALUES (1, '张三', 1000);
INSERT INTO `test_account` VALUES (2, '李四', 1000);
INSERT INTO `test_account` VALUES (3, '王五', 1000);

查看当前数据库隔离级别(详见下文):

select @@tx_isolation;
# 结果:READ-COMMITTED
# 设置事务的隔离级别
# set tx_isolation='隔离级别';
# 我们简单做下脏读的复现,开启俩个事务,第一个窗口做数据修改,但不提交:
start transaction;
UPDATE test_account set money = money - 100 WHERE id = 1;
# 第二个窗口做数据查询,利用查询到的值去做数据处理:
start transaction;
SELECT money from test_account WHERE id = 1;
结果:1000
那么这时读到的数据是不准确的,这就是脏读

我们解决并发读的问题可以设置隔离级别解决问题:

隔离级别

脏读

不可重复读

幻读

读未提交 (Read uncommitted)

读已提交 (Read committed)

×

可重复读 (Repeatable read)

×

×

可串行化 (Serializable )

×

×

×

Spring传播行为

Spring传播行为

介绍

REQUIRED

支持当前事务,如果不存在,就新建一个

SUPPORTS

支持当前事务,如果不存在,就不使用事务

MANDATORY

支持当前事务,如果不存在,抛出异常

REQUIRES_NEW

如果有事务存在,挂起当前事务,创建一个新的事务

NOT_SUPPORTED

以非事务方式运行,如果有事务存在,挂起当前事务

NEVER

以非事务方式运行,如果有事务存在,抛出异常

NESTED

如果当前事务存在,则嵌套事务执行(嵌套式事务)

事务的传播行为不是jdbc规范中的定义。传播行为主要针对实际开发中的问题

分布式事务

为什么要有分布式事务?

本地事务只能解决同一工程中的事务问题,而现在的场景更加复杂,关系到多个服务,怎么保证要么都成功,要么都失败?

分布式系统异常除了本地事务那些异常之外,还有:机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的TCP、存储数据丢失等,这时候就需要引入分布式事务。

分布式事务出现的场景

  • 不同的服务,不同数据库
  • 相同的服务,不同数据库
  • 不同的服务,相同数据库

分布式事务基础

数据库的 ACID 四大特性,已经无法满足我们分布式事务,这个时候有很多大佬提出一些新的理论。

CAP:

分布式存储系统的CAP原理(分布式系统的三个指标):

  • Consistency(一致性):在分布式系统中的所有数据备份,在同一时刻是否同样的值
  • 对于数据分布在不同节点上的数据来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
  • Availability(可用性):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(要求数据需要备份)
  • Partition tolerance(分区容忍性):大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。

CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们无法避免的。所以我们只能在一致性和可用性之间进行权衡,没有系统能同时保证这三点。要么选择CP、要么选择AP。

BASE:

BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。接下来看看BASE中的三要素:

  • Basically Available(基本可用
  • 基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。 电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
  • Soft state(软状态)
  • 软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。
  • Eventually consistent(最终一致性)
  • 最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

BASE模型是传统ACID模型的反面,不同于ACID,BASE强调牺牲高一致性,从而获得可用性,数据允许在一段时间内的不一致,只要保证最终一致就可以了

分布式事务解决方案

主流的解决方案如下:

  • 基于XA协议的两阶段提交(2PC)
  • TCC编程模式
  • 消息事务+最终一致性

两阶段提交(2PC):

2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。

第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交. 第二阶段:事务协调器要求每个数据库提交数据。 其中,如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务中的那部分信息。

XA 是一个两阶段提交协议,又叫做 XA Transactions。 目前主流数据库均支持2PC,XA协议。

XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换会导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

TCC补偿式事务:

TCC补偿式事务是一种编程式分布式事务。

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。TCC模式要求从服务提供三个接口:Try、Confirm、Cancel。

  • Try:主要是对业务系统做检测及资源预留
  • Confirm:真正执行业务,不作任何业务检查;只使用Try阶段预留的业务资源;Confirm操作满足幂等性。
  • Cancel:释放Try阶段预留的业务资源;Cancel操作满足幂等性。

整个TCC业务分成两个阶段完成:

第一阶段:主业务服务分别调用所有从业务的try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败,进入第二阶段。

第二阶段:活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功,则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作。

消息事务+最终一致性:

基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。

但是A和B并不是严格一致的,而是最终一致的,我们在这里牺牲了一致性,换来了性能的大幅度提升。当然,这种玩法也是有风险的,如果B一直执行不成功,那么一致性会被破坏,具体业务具体分析

总结:

  • 高并发最终一致:消息事务+最终一致性
  • 低并发基本一致:二阶段提交
  • 高并发强一致:没有解决方案

分布式事务框架-seata

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

结构

Seata有3个基本组件:

  • Transaction Coordinator(TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
  • Transaction Manager(TM):事务管理器,控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
  • Resource Manager(RM):资源管理器,控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

生命周期(重点)

  • TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
  • XID 在微服务调用链路的上下文中传播。
  • RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
  • TM 向 TC 发起针对 XID 的全局提交或回滚决议。
  • TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

引入seata并使用

  • 把undo_log表导入数据库中
  • 部署seata-server启动
  • 在项目中添加seata依赖:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-seata</artifactId>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-all</artifactId>
    <version>1.2.0</version>
</dependency>
  • 配置文件中添加配置:
spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
  • 引入steata配置文件:registry.conf和file.conf

java代码

@Configuration
public class DataSourceConfig {
  @Bean
  @ConfigurationProperties(prefix = "spring.datasource")
  public DruidDataSource druidDataSource() {
    return new DruidDataSource();
  }
  /**
  * 需要将 DataSourceProxy 设置为主数据源,否则事务无法回滚
  *
  * @param druidDataSource The DruidDataSource
  * @return The default datasource
   */
  @Primary
  @Bean("dataSource")
  public DataSource dataSource(DruidDataSource druidDataSource) {
    return new DataSourceProxy(druidDataSource);
  }
}

使用

使用stata框架非常的简单,只需要引入注解就好了

  • 分支事务方法上:@Transactional
  • 全局事务方法上:@GlobalTransactional
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
16天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
25天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
25天前
|
消息中间件 Java 数据库连接
Java 反射最全详解 ,框架设计必掌握!
本文详细解析Java反射机制,包括反射的概念、用途、实现原理及应用场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 反射最全详解 ,框架设计必掌握!
|
2月前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
52 1
Spring 框架:Java 开发者的春天
|
24天前
|
机器学习/深度学习 自然语言处理 并行计算
DeepSpeed分布式训练框架深度学习指南
【11月更文挑战第6天】随着深度学习模型规模的日益增大,训练这些模型所需的计算资源和时间成本也随之增加。传统的单机训练方式已难以应对大规模模型的训练需求。
79 3
|
2月前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
|
27天前
|
缓存 Java 数据库连接
Hibernate:Java持久层框架的高效应用
通过上述步骤,可以在Java项目中高效应用Hibernate框架,实现对关系数据库的透明持久化管理。Hibernate提供的强大功能和灵活配置,使得开发者能够专注于业务逻辑的实现,而不必过多关注底层数据库操作。
13 1
|
2月前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
28天前
|
机器学习/深度学习 并行计算 Java
谈谈分布式训练框架DeepSpeed与Megatron
【11月更文挑战第3天】随着深度学习技术的不断发展,大规模模型的训练需求日益增长。为了应对这种需求,分布式训练框架应运而生,其中DeepSpeed和Megatron是两个备受瞩目的框架。本文将深入探讨这两个框架的背景、业务场景、优缺点、主要功能及底层实现逻辑,并提供一个基于Java语言的简单demo例子,帮助读者更好地理解这些技术。
57 2
|
2月前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
59 5
下一篇
无影云桌面