Spring事务与分布式事务

2023-10-29

一、事务的具体定义

事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交,只要其中任一操作执行失败(出现异常),都将导致整个事务的回滚。简单地说,事务提供一种“要么什么都不做,要么做全套(All or Nothing)”机制。

明白上面的这几句话,ACID就不用看了,ACID就是对这句话的一个解释。

  • 原子性(Atomicity): 一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。
  • 一致性(Consistency):数据库总是从一个一致性的状态转换到另一个一致性的状态。在事务开始前后,数据库的完整性约束没有被破坏。例如违反了唯一性,必须撤销事务,返回初始状态。
  • 隔离性(Isolation): 每个读写事务的对象对其他事务的操作对象能相互分离,即:事务提交前的数据对其他事务是不可见的,通常内部加锁实现。不同的隔离级别加不同的锁。
  • 持久性(Durability): 一旦事务提交,则其所做的修改会永久保存到数据库。

二、并发环境下的数据库事务

2.1 事务并发执行会出现的问题

我们先来看一下事务并发,数据库可能会出现的问题:

  • 更新丢失(问题严重)
    当有两个并发执行的事务,更新同一行数据,那么有可能一个操作会把另一个操作的更新数据覆盖掉。

  • 脏读 (问题严重)
    一个事务读到另一个尚未提交的事务中的数据,即读到了事务的处理过程中的数据,而不是结果数据。 该数据可能会被回滚从而失效。 如果第一个事务拿着失效的数据去处理那就发生错误了。

  • 不可重复读 (一般来说可以接受,比如你交话费,交完就查看可能没到账,过2分钟再查就到账了)
    不可重复读的含义:一个事务对同一行数据读了两次,却得到了不同的结果。它具体分为如下两种情况:
    虚读:在事务1两次读取同一记录的过程中,事务2对该记录进行了修改,从而事务1第二次读到了不一样的记录。
    幻读:事务1在两次查询的过程中,事务2对该表进行了插入、删除操作,从而事务1第二次查询的结果数量发生了变化。

不可重复读 与 脏读 的区别?
脏读读到的是尚未提交的数据,而不可重复读读到的是已经提交的数据,只不过在两次读的过程中数据被另一个事务改过了。

2.3 如何解决并发过程中事务问题(事务隔离)

数据库一共有如下四种隔离级别:

  • Read uncommitted 读未提交
    在该级别下,一个事务对一行数据修改的过程中,不允许另一个事务对该行数据进行修改,但允许另一个事务对该行数据读。
    因此本级别下,不会出现更新丢失,但会出现脏读、不可重复读。

  • Read committed 读提交 (oracle、sqlserver默认的隔离级别)
    在该级别下,未提交的写事务不允许其他事务访问该行,因此不会出现脏读;但是读取数据的事务允许其他事务的访问该行数据,因此会出现不可重复读的情况。

  • Repeatable read 重复读 (mysql的默认隔离级别)
    简单说就是:一个事务开始读或写数据时,不允许其他事务对该数据进行修改。在该级别下,读事务禁止写事务,但允许读事务,因此不会出现同一事务两次读到不同的数据的情况(不可重复读),且写事务禁止其他一切事务。这个级别无法解决幻读问题

  • Serializable 序列化
    该级别要求所有事务都必须串行执行,因此能避免一切因并发引起的问题,但效率很低。

隔离级别 出现更新丢失问题 出现脏读问题 出现虚读问题 出现幻读问题 实现方式
Read uncommitted 读未提交 No Yes Yes Yes 排他写锁
Read committed 读提交 No No Yes Yes 瞬间共享读锁与排他写锁
Repeatable read 重复读 No No No Yes 共享读锁与排他写锁
Serializable 序列化 No No No No

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,应该由应用程序员采用悲观锁或乐观锁来控制。

三、Spring事务传播行为

举例说明

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。

用伪代码说明:

ServiceA {
         @Transactional(Propagation=XXX)
         void methodA() {
             //其他持久层操作数据库
             ServiceB.methodB();
         }
}
      
ServiceB {
         @Transactional(Propagation=YYY)
         void methodB() {
            //持久层操作数据库
         }
}

代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transactional(Propagation=YYY)设置决定。

Spring中七种事务传播行为

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

定义非常简单,也很好理解,下面我们就进入代码测试部分,验证我们的理解是否正确。

回答一个问题:当一个Service函数里面既使用Mybatis Mapper,又使用JdbcTemplate操作同一个数据库,能保证二者操作的整体事务么? 答案是可以的,因为事务控制器是在Spring的层面控制的,与持久层框架无关。

四、Spring @Transactional 注解

新建的Spring Boot项目中,一般都会引用spring-boot-starter或者spring-boot-starter-web,而这两个起步依赖中都已经包含了对于spring-boot-starter-jdbcspring-boot-starter-data-jpa的依赖。 当我们使用了这两个依赖的时候,框架会自动默认分别注入DataSourceTransactionManagerJpaTransactionManager

所以我们不需要任何额外配置就可以用@Transactional注解进行事务的管理。在spring框架内实现多个数据库持久层操作的事务,我们只需要在方法或类添加@Transactional注解即可。@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。

@Transactional
public int xxx(){
    // 增删改持久层操作一
    // 增删改持久层操作二
    // ……
}
​

当多个持久层操作在同一个Service层方法上时,能保证多个持久层操作要么都成功,要么都失败。

属性名 说明
value 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
propagation 事务的传播行为,默认值为 REQUIRED。
isolation 事务的隔离度,默认值采用 DEFAULT。
timeout 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollback-for 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for 抛出 no-rollback-for 指定的异常类型,不回滚事务。

五、分布式事务

笔者自己将分布式事务分为两种:跨服务的分布式事务,跨库的分布式事务。

5.1.跨库的分布式事务

跨库的分布式事务:一个服务层函数,需要同时操作两个数据库。我们之前给大家讲的例子都是这一种,实际上总的思路:就是有一个“事务管理器”对象统一管理多个数据源事务的提交与回滚。事务管理器协调多数据源进行两段式提交。

为了大家方便理解:我以小故事方式给大家讲一下两段式提交:

  • 背景:以缉毒警察抓捕专案毒贩为背景,目前3位毒贩A、B、C分别住在不同的住址,目前要实施抓捕。将缉毒大队分成三个组,组A、组B、组C分别针对毒贩A、B、C,三个小组统一由“缉毒大队长”协调指挥。
    • 三名毒贩住在不同的住址,体现的是“分布式”,3个数据库
    • “缉毒大队长”代表的是“事务管理器”TransctionManager,负责抓捕这个事务的协调指挥工作。
    • 三个抓捕小组,代表的是XAResourceManager,是XA/JTA两阶段提交规范的单一资源操作的执行者。
  • 抓捕的要求是:把三名毒贩同时抓获,不能先抓A,如果A抓捕失败打草惊蛇,可能给B、C报信。要么就全抓到,要么就一个也别抓,免得打草惊蛇。
    • 抓捕的要求和我们对于“分布式”事务的要求是一样的,多数据库操作要么都成功,要么都失败。
  • 抓捕的步骤:
    • 第一步:三个小组分别靠近毒贩A、B、C的住址,然后等待“缉毒大队长”协调指挥。“缉毒大队长”询问A小组是否完成准备抓捕工作,A小组回复:准备完毕。以此类推,“缉毒大队长”询问B、C两个抓捕小组,这三个组都准备完成了,并且没有异常情况发生,第一阶段工作完毕。即:两阶段提交的第一阶段:预提交
    • 如果任何一个小组发现异常,整个行动计划立刻取消。三个抓捕小组同时收队,这个可以认为是数据库事务回滚。
    • 第二步:三个小组已经全部准备好了,“缉毒大队长”下命令:“抓捕”。三个抓捕小组同时行动,分别抓捕三名毒贩。确保全部落网,一个也跑不掉。这就好比事务两阶段提交的第二阶段:整体提交

5.2.跨服务的分布式事务

跨服务分布式事务: 也就是说我在做一个服务A的时候,需要通过HTTP网络请求调用多个其他服务,有可能第一个服务B成功了,第二个服务C执行失败了。我们期望的结果是:服务B和服务C都成功。这种分布式单纯的依靠数据库层面就很难解决了。


这种情况一般都是通过最终一致性的方式解决。比如:通过MQ消息队列,给服务B发消息,服务B执行,然后真的做持久化操作数据入库了。

给服务C发消息,如果服务C执行失败,这个消息就会存在MQ里面,依照一定的策略还会发给服务C,直到服务C成功为止。这种策略被叫做“ Exactly-once”,精确的保证成功一次并且只成功一次。这样保障操作结果的最终一致性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring事务与分布式事务 的相关文章

  • 从 postgres 数据库中选择 Spring 数据与 JDBI 的基准测试

    我想比较 Spring 数据与 JDBI 的性能 我使用了以下版本 Spring Boot 2 2 4 RELEASE vs JDBI 3 13 0 测试相当简单 select from admin table 并转换为 Admin 对象列
  • Spring Boot 健康执行器 - 什么时候上线?

    我找不到任何有关 Springs Health Actuator 何时返回 UP 状态的文档 你能依靠一切吗 Components正在初始化 会不会 Controller准备好满足请求了吗 为了测试应用程序上下文是否已加载 您可以执行此自定
  • 使用 Spring 测试数据库关闭

    我需要在数据库 连接 关闭数据库 错误期间对应用程序行为进行 Spring 测试 有没有办法从 Spring 单元测试中关闭 终止或启动 H2 内存数据库 如果测试数据库连接 这里很旧 但仍然很好关于该主题的博客 https keyhole
  • Spring,如何使用 websocket 向连接的客户端广播消息?

    我正在尝试在我的应用程序中使用 websockets 我已经遵循了这个教程 http spring io guides gs messaging stomp websocket http spring io guides gs messag
  • Wicket+Spring+JPA+Hibernate:未找到持久性单元

    我正在使用 Wicket Spring JPA Hibernate 开发一个 Web 应用程序 这是我使用此设置的第一个项目 我想我可能犯了一些错误 我收到以下错误 找不到名为 ApplicationEntityManager 的持久性单元
  • 升级到 1.3.0.RELEASE 时出现 Spring Boot 问题

    您好 我刚刚升级到 spring boot 1 3 0 RELEASE 再次运行相同版本时出现这个奇怪的错误 14 43 52 503 main INFO c test whf service HfServices Starting up
  • 如何在JSP中显示对象的数据

    我已通过注册表将一些用户详细信息存储到数据库 hibernate 和 spring 中 我想在单独的 JSP 页面中显示所有用户的用户详细信息 有人可以告诉我该怎么做吗 下面是我的控制器代码 Controller public class
  • 服务器遇到意外情况,无法满足请求

    我正在尝试显示来自数据库的数据 但是显示错误为 服务器遇到意外情况 无法正常运行 满足请求 例外 org apache jasper JasperException An exception occurred processing JSP
  • Spring AOP 排除一些类

    我使用 Spring AspectJ 来记录方法执行统计信息 但是 我想从中排除一些类和方法而不更改切入点表达式 为了排除某些方法 我创建了一个自定义注释 用于过滤掉 但是我无法对课程做同样的事情 这是我的方面定义 Around execu
  • Spring:从另一个bean访问bean属性

    我有两颗豆子 配置管理器 public class ConfigurationManager private Configuration configuration public void init Loads a configuratio
  • Spring 中 Mockito 的间谍对象

    当我尝试在单元测试中监视对象时 我有一个例外 这是我的单元测试文件 RunWith SpringJUnit4ClassRunner class ContextConfiguration locations classpath spring
  • Junit4 + Spring 2.5:断言抛出“NoClassDefFoundError”

    我一直在使用 Spring 在 Junit4 中编写测试代码 并且得到了这个有趣的行为 如果我的测试像这样通过 那么一切都很好 Test public void truthTest assertTrue true Ok 但是 如果我的测试失
  • 将 Spring 的 @Scheduled 注解与特定执行器一起使用

    如何告诉我的 Spring 计划方法使用特定的执行器运行 例如 这是我的 spring 调度程序方法之一 Scheduled fixedRate 1000 public void scheduleJobs doThese 这是我的 Java
  • 通过 Java Spring 构建 sitemap.xml [重复]

    这个问题在这里已经有答案了 我正在通过 Spring MVC 构建 sitemap xml XmlRootElement name urlset public class XmlUrlSet XmlElements XmlElement n
  • 如何在 spring-ws 中解析 SoapFaultClientException

    我正在使用 spring ws 2 3 1 在为 Web 服务创建客户端时 有时我得到SoapFaultClientException像下面这样
  • RuntimeException:必须提供 MemberAccess 实现

    我正在使用一个访问公共字段的模板Customer像这样的对象 div div div div div div div div div div 然而 当我打电话时process on the TemplateEngine templateEn
  • Spring Boot、logback 和logging.config 属性

    我正在 Spring Boot 项目中实现日志记录logback图书馆 我想根据我的 Spring 配置文件加载不同的日志配置文件 属性spring pofiles active 我有3个文件 logback dev xml logback
  • 理解 Spring AOP [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在使用 Spring 3 0 框架 但仍然是新手 谁能通俗地解释一下什么是AOP编程 一个简短的例子肯定会有帮助 Spring 如
  • 使用 Atomikos 进行两阶段提交 (2PC) 配置

    我正在创建一个示例应用程序来测试两阶段提交 2PC 我从互联网上获取了此处使用的代码位 我使用 Spring Hibernate 和 Atomikos 并以 MySQL 作为后端 我正在使用两个数据库 并故意使对第二个数据库的调用失败 以检
  • 测试 Spring 端点时如何防止 NestedServletException?

    我在尝试着测试安全配置我的一些端点受到保护 PreAuthorize oauth2 hasScope scope 当使用不具有所需范围的访问令牌通过 Postman 访问此类端点时 将返回以下内容并带有 HTTP 状态代码 403 禁止 e

随机推荐

  • python: read excel and export excel

    PythonAppReadExcel py edit geovindu Geovin Du 涂聚文 date 2023 06 13 保险 This is a sample Python script python exe m pip ins
  • 关于华为手机使用MTK刷机时出现failed to get PMT info的解决办法

    华为手机USB驱动 https pan baidu com s 1dDBZLSH 华为手机系统修复工具 https pan baidu com s 1dDBZLSH 最近在折腾手中的一部华为荣耀3C手机 前段时间在手机维修店给维修时 给我升
  • HTML 表格跨行跨列

    HTML和CSS第一天 8 8跨行跨列表格 次重点 必须掌握 table table
  • PAT01-Switch..case用法

    package PAT01 import java util Scanner public class Main public static void main String args Scanner in new Scanner Syst
  • es bulk java_23个最有用的ES检索技巧(Java API实现)

    前言 本文是对 23个最有用的Elasticseaerch检索技巧 一文提到的ES检索技巧进行 Java API 的简单实现 但仅限于简单实现 并不考虑包括参数校验 异常处理 日志处理 安全等问题 仅供参考 运行环境 JDK version
  • oneAPI技术在奥斯卡颁奖礼上拿过奖!背后的神秘工具竟是它

    各位小伙伴们 作为各位开发人员亲密的朋友 今天要小小 炫耀 一下 那就是oneAPI 曾经在奥斯卡颁奖礼上拿过奖 oneAPI作为英特尔的革命性计划 以提供统一的跨架构软件编程模型被大众熟知 在电影行业的奥斯卡颁奖礼上获奖 这是怎么回事呢
  • QT信号槽传递参数技巧

    信号槽如何传递参数 或带参数的信号槽 利用Qt进行程序开发时 有时需要信号槽来完成参数传递 带参数的信号槽在使用时 有几点需要注意的地方 下面结合实例进行介绍 第一点 当信号与槽函数的参数数量相同时 它们参数类型要完全一致 信号 cpp v
  • 力扣-图解算法数据结构

    常见的数据结构可分为 线性数据结构 与 非线性数据结构 具体为 数组 链表 栈 队列 树 图 散列表 堆 数组 数组是将相同类型的元素存储于连续内存空间的数据结构 其长度不可变 如下图所示 构建此数组需要在初始化时给定长度 并对数组每个索引
  • unity3d math 常用的数学

    1 计算游戏中敌人被击退的方向 类似 Vector3 lhs Vector3 this parentChara MoveDirection this parentChara MoveSpeed Vector3 rhs component p
  • 二十四史全译本

    现在正在读战争与和平 觉得这里面写了好多的历史的东西 所以想到以前读过的历史书籍 于是想找本历史书籍来看看 后来发现了二十四史全译本 就是对古文的历史解释为白话文 这是官方承认的正史了 应该值得一睹 所以下定决心 看一看这些历史书籍 201
  • 精品课程:Node+TS+Koa+Vue 商城全栈(前后端)开发

    课程目录 Node TS Koa商城全栈开发远程课介绍视频 Symbol与作用域 解构赋值与扩展运算符 字符串 数字与对象扩展 迭代 函数扩展 箭头函数 集合 Set对象 let和const 变量的解构赋值 数据结构Set 数据结构Map
  • Python 爬虫批量爬取网页图片保存到本地

    其实和爬取普通数据本质一样 不过我们直接爬取数据会直接返回 爬取图片需要处理成二进制数据保存成图片格式 jpg png等 的数据文本 现在贴一个url https img ivsky com img tupian t 201008 05 b
  • Python version 2.7 required, which was not found in the registry

    转自 http www cnblogs com min0208 archive 2012 05 24 2515584 html 安装setuptools的时候 不能再注册表中识别出来python2 7 在网上找了方法 仅作笔记 供下次使用
  • openwrt源下载太慢,make太慢等问题的处理

    目录 前言 一 在github获取源码 二 使用gitee获取源码 1 注册gitee 2 注册github 3 将openwrt官方github的源码fock到自己的github中 4 将github的openwrt源码导入到gitee
  • 一文看完2018苹果秋季新品发布会,你想知道的问题这里都有答案!

    苹果2018秋季新品发布会结束了 此处发布会看完下来内心毫无波澜 并没有多少惊艳到人的地方 倒是处处看到了国产手机发布会的影子 话不多说 下文给大家汇总一下本次苹果新品发布会的重点 命名有国产手机的气息 和此前网传的名字有一些出入 本次发布
  • R语言笔记二(控制结构)

    Control Structures Control structures in R allow you to control the flow of execution of the program depending on runtim
  • 正则表达式—HTML中的匹配

    从HTML中文本中提取Email地址和http URL 是在做爬虫时候的经常用到的技术 虽然变成语言本身可以帮助我们找到他们 但是用正则表达式来匹配也是很有用和具有实际意义的方法 一 匹配HTML Tag HTML不是有特别严格编程要求的
  • ISE14.7使用教程(一个完整工程的建立)

    ISE14 7使用教程 一个完整工程的建立 博主提到 黑金xlinix FPGA 黑金动力社区 http www heijin org 如需转载 请注明出处http www cnblogs com kingst 黑金官网 Http www
  • Linux ❉ ntpdate命令详解

    一 介绍 ntpdate命令用于同步更新互联网时间 或者NTP服务器时间 NTP服务器 Network Time Protocol NTP 是用来使计算机时间同步化的一种协议 它可以使计算机对其服务器或时钟源 如石英钟 GPS等等 做同步化
  • Spring事务与分布式事务

    一 事务的具体定义 事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元 组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交 只要其中任一操作执行失败 出现异常 都将导致整个事务的回滚 简单地说 事务提供一种 要么