Springboot 大事务问题的常用优化方案

news2025/2/23 14:07:52

🏷️个人主页:牵着猫散步的鼠鼠 

🏷️系列专栏:Java全栈-专栏

🏷️个人学习笔记,若有缺误,欢迎评论区指正

目录

1.前言

2.什么是大事务

3.解决办法

3.1.少用@Transactional注解

3.2..将查询(select)方法放到事务外

3.3事务中避免远程调用

3.4事务中避免一次性处理太多数据

3.5非事务执行

3.6异步处理

4.总结


1.前言

在分享解决办法之前,先看看系统中如果出现大事务可能会引发哪些问题

从上图可以看出,如果系统中出现大事务的问题,那么情况不容乐观。

因此,在实际项目开发中,我们应该尽量避免大事务的情况。

2.什么是大事务

大事务是指运行时间比较长,操作的数据比较多的事务123。例如,执行超过5秒、10秒、1分钟等。大事务的产生原因包括操作的数据比较多、大量的锁竞争、事务中有其他非数据库的耗时操作等。

3.解决办法

3.1.少用@Transactional注解

在实际项目中,开启事务功能是非常常见的做法。在业务方法上加上@Transactional注解,可以方便地开启事务功能,这种做法被称为声明式事务。

@Transactional(rollbackFor=Exception.class)
   public void save(User user) {
         doSameThing...
   }

为什么说要少用@Transactional注解?

  1. 我们知道@Transactional注解是通过spring的aop起作用的,但是如果使用不当,事务功能可能会失效。
  2. @Transactional注解一般加在某个业务方法上,会导致整个业务方法都在同一个事务中,粒度太粗,不好控制事务范围,是出现大事务问题的最常见的原因。

那可以使用编程式事务,在spring项目中使用TransactionTemplate类的对象,手动执行事务。


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         transactionTemplate.execute((status) => {
            doSameThing...
            return true;
         })
   }

3.2..将查询(select)方法放到事务外

查询(select)方法一般情况下是不需要事务的,所以应该放到事务外!

@Transactional(rollbackFor=Exception.class)
   public void save(User user) {
         query1();
         query2();
         add1();
         update2();
   }

可以将query1和query2两个查询方法放在事务外执行,将真正需要事务执行的代码才放到事务中,比如:add1和update2方法,这样就能有效的减少事务的粒度。

可以用这个TransactionTemplate来解决!


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         query1();
         query2();
         transactionTemplate.execute((status) => {
            add1();
            update2();
            return Boolean.TRUE;
         })
   }

但是如果你实在还是想用@Transactional注解,该怎么拆分呢?

public void save(User user) {
         query1();
         query2();
         doSave();
    }
   
    @Transactional(rollbackFor=Exception.class)
    public void doSave(User user) {
       add1();
       update2();
    }

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务。是因为声明式事务来实现事务控制,当我们通过this调用普通方法时,是通过原始对象而不是代理对象调用的方法,导致事务

不过这边事务是失效的,因为直接方法调用使用的还是原始对象,所以事务不会生效。

那该怎么解决呢?

1.新加Service方法

这个方法非常简单,只需要新加Service方法,把@Transactional注解加到新Service方法上,把需要事务执行的代码移到新方法中。具体代码如下:

@Servcie
  publicclass ServiceA {
     @Autowired
     prvate ServiceB serviceB;
  
     public void save(User user) {
           query1();
           query2();
           serviceB.doSave(user);
     }
   }
   
   @Servcie
   publicclass ServiceB {
   
      @Transactional(rollbackFor=Exception.class)
      public void doSave(User user) {
         add1();
         update2();
      }
   
   }

2.在该Service类中注入自己

在该Service类中注入自己也是一种选择,如下:

@Servcie
  publicclass ServiceA {
     @Autowired
     prvate ServiceA serviceA;
  
     public void save(User user) {
           query1();
           query2();
           serviceA.doSave(user);
     }
     
     @Transactional(rollbackFor=Exception.class)
     public void doSave(User user) {
         add1();
         update2();
      }
   }

spring ioc内部的三级缓存保证了它,不会出现循环依赖问题。

3.在该Service类中使用AopContext.currentProxy()获取代理对象

通过在该Service类中使用AOPProxy获取代理对象,实现相同的功能。如下:

@Servcie
  publicclass ServiceA {
  
     public void save(User user) {
           query1();
           query2();
           ((ServiceA)AopContext.currentProxy()).doSave(user);
     }
     
     @Transactional(rollbackFor=Exception.class)
     public void doSave(User user) {
         add1();
         update2();
      }
   }

3.3事务中避免远程调用

网络不稳定,远程调其他系统响应时间可能比较长,这就是大事务!

当然,发MQ消息,或者连接redis、mongodb保存数据等也属于远程调用哦!

@Transactional(rollbackFor=Exception.class)
   public void save(User user) {
         callRemoteApi();
         addData1();
   }

远程调用的代码可能耗时较长,切记一定要放在事务之外。


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         callRemoteApi();
         transactionTemplate.execute((status) => {
            addData1();
            return Boolean.TRUE;
         })
   }

不过这就需要建立:重试+补偿机制,达到数据最终一致性了。

3.4事务中避免一次性处理太多数据

如果一个事务中需要处理的数据太多,也会造成大事务问题。

你可能会一次批量更新1000条数据,这样会导致大量数据锁等待,特别在高并发的系统中问题尤为明显! 那怎么解决呢?

用分页处理,1000条数据,分20页,一次只处理50条数据,这样可以大大减少大事务的出现。

3.5非事务执行

在使用事务之前,我们都应该思考一下,是不是所有的数据库操作都需要在事务中执行?


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         transactionTemplate.execute((status) => {
            addData();
            addLog();
            updateCount();
            return Boolean.TRUE;
         })
   }

上面的例子中,其实addLog增加操作日志方法 和 updateCount更新统计数量方法,是可以不在事务中执行的,因为操作日志和统计数量这种业务允许少量数据不一致的情况。


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         transactionTemplate.execute((status) => {
            addData();           
            return Boolean.TRUE;
         })
         addLog();
         updateCount();
   }

当然大事务中要鉴别出哪些方法可以非事务执行,其实没那么容易,需要对整个业务梳理一遍,才能找出最合理的答案。

3.6异步处理

我们都知道,方法同步执行需要等待方法返回,如果一个事务中同步执行的方法太多了,势必会造成等待时间过长,出现大事务问题。

看看下面这个列子:


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         transactionTemplate.execute((status) => {
            order();
            delivery();
            return true;
         })
   }

order方法用于下单,delivery方法用于发货,是不是下单后就一定要马上发货呢?

答案是否定的。

这里发货功能其实可以走mq异步处理逻辑。


   @Autowired
   private TransactionTemplate transactionTemplate;
   
   ...
   
   public void save(final User user) {
         transactionTemplate.execute((status) => {
            order();
            return Boolean.TRUE;
         })
         sendMq();
   }

4.总结

以上就是个人总结的解决大事务的一些方法,如果还有更多的方法和意见,欢迎评论区指出~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1586125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医疗图像分割 | 基于Pyramid-Vision-Transformer算法实现医疗息肉分割

项目应用场景 面向医疗图像息肉分割场景,项目采用 Pytorch Pyramid-Vision-Transformer 深度学习算法来实现。 项目效果 项目细节 > 具体参见项目 README.md (1) 模型架构 (2) 项目依赖,包括 python 3.8、pytorch 1.7.1、torchvision 0.8.2(3) 下载…

【实战】ZLMediaKit问题解决

项目中遇到的问题 1.不带音频的rtsp转rtmp后,出现了音频 1.1判断元素rtsp是否有音频的方法 使用vlc进行访问rtsp流,看如图位置: 音频 -> 音轨 ,是否为灰色,为灰色就是不带音频 1.2 解决方法 在zlmediakit的web页面进行全局配置修改如图, 1.将3和4处修改为 否,再保存, …

网络协议——RSTP(快速生成树)与MSTP(多实例生成树)

一. RSTP 1. STP的不足 1、依靠计时器超时的方式进行收敛导致它的收敛时间需要30到50秒 2、端口状态和端口角色没有细致区分,指导数据转发依靠的不是端口状态而是端口所扮演角色。 3、如果拓扑频繁变化导致用户通信质量差,甚至通信中断&#xf…

MyBatis中的动态SQL的用法

前言:我们要想在Spring Boot环境下使用动态SQL,必须先在application.yml中添加配置 mybatis:mapper-locations: classpath:mapper/**Mapper.xml 并且新建一个xml文件,路径及写法按照配置好的形式写 在新建好的xml文件中复制进去以下代码&a…

Golang——方法

一. 方法定义 Golang方法总是绑定对象的实例,并隐式将实例作为第一实参。 只能为当前包内命名类型定义方法参数receiver可以任意命名。如方法中未曾使用,可省略参数名参数receiver类型可以是T或*T。基类型T不能是接口或指针类型(即多级指针)不支持方法重…

【JAVASE】抽象类和接口及其抽象类和接口的区别

✅作者简介:大家好,我是橘橙黄又青,一个想要与大家共同进步的男人😉😉 🍎个人主页:再无B~U~G-CSDN博客 目标: 1. 抽象类 2. 接口 3. Object 类 1. &am…

性能测试--数据库慢 SQL 语句分析

一 慢 SQL 语句的几种常见诱因 1. 无索引或索引失效 ​ 当查询基于一个没有索引的列进行过滤、排序或连接时,数据库可能被迫进行全表扫描,即逐行检查所有数据,导致性能显著下降。 ​ 虽然我们很多时候建立了索引,但在一些特定的…

第3章 存储系统(2)

3.3 主存储器与CPU连接 3.3.1 连接原理 现代计算机的MAR和MDR都在CPU内部。 (1)主存储器通过数据总线,地址总线,控制总线与CPU连接。 (2)数据传输率数据总线宽度*总线频率。 (4)控制总线(读写线)控制读写操作。 3.3.2 主存的扩展 数据总线宽度等于存储字长 1.位扩展法【增加…

【软件测试】个人博客系统测试

个人博客系统测试 一、项目背景1.1 技术背景1.2 功能背景 二、自动化测试2.1 什么是自动化测试2.2 通过使用selenium进行自动化测试的编写(Java实现)2.3 编写测试用例,执行自动化测试2.3.1 输入用户名:test,密码:123,登录成功2.3.…

Java | Leetcode Java题解之第20题有效的括号

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isValid(String s) {int n s.length();if (n % 2 1) {return false;}Map<Character, Character> pairs new HashMap<Character, Character>() {{put(), ();put(], [);put(}, {);}};Deque<…

基于SpringBoot+Vue的工厂生产设备维护管理系统(源码+文档+部署+讲解)

一.系统概述 随着社会的发展&#xff0c;系统的管理形势越来越严峻。越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得工厂生产设备维护信息&#xff0c;因此&#xff0c;设计一种安全高效的工厂生产设备…

Vue中key的原理以及diff算法

简介&#xff1a;Vue的key用于在虚拟DOM中标记节点&#xff0c;方便后面的diff对比算法进行对比&#xff0c;提升效率。 Vue的vm或者vc实例一共管理着3个DOM对象&#xff0c;分别他的模板对应的真实DOM、真实DOM的备份、以及重新生成的新的DOM&#xff0c;后两个可以看成是虚拟…

Scaffold-GS 代码阅读笔记

1. 系统启动部分 使用 python 中的 parser 库 为配置系统的参数设定, 和3DGS 类似&#xff0c;并且使用safe_state(args.quiet) 函数 为每一次的 log 输出加上对应的 时间戳 ## 配置参数的设定lp ModelParams(parser)op OptimizationParams(parser)pp PipelineParams(pars…

每日一题(leetcode1702):修改后的最大二进制字符串--思维

找到第一个0之后&#xff0c;对于后面的子串&#xff08;包括那个0&#xff09;&#xff0c;所有的0都能调上来&#xff0c;然后一一转化为10&#xff0c;因此从找到的第一个0的位置开始&#xff0c;接下来是&#xff08;后半部分子串0的个数-1&#xff09;个1&#xff0c;然后…

移动WEB开发之响应式布局

一、响应式开发 1、响应式开发原理 就是使用媒体查询针对不同宽度的设备进行布局和样式的设置&#xff0c;从而适配不同设备的目的。 2、响应式布局容器 响应式布局容器需要一个父级作为布局容器&#xff0c;来配合子集元素来实现变化效果。 原理就是在不同屏幕下通过媒体查询…

OpenHarmony 资源调度之内存管理源码分析

作者&#xff1a;张守忠 1 内存管理简介 内存管理部件位于全局资源调度管控子系统中&#xff0c;基于应用的生命周期状态&#xff0c;更新进程回收优先级列表&#xff0c;通过内存回收、查杀等手段管理系统内存&#xff0c;保障内存供给。 1.1 内存管理框架 内存管理部件主要…

你一定不能错过的多模态大模型!阿里千问开源Qwen-VL!具备图文解读等能力

1. Qwen-VL简介 1.1. 介绍 Qwen-VL的多语言视觉语言模型系列,基于Qwen-7B语言模型。该模型通过视觉编码器和位置感知的视觉语言适配器,赋予语言模型视觉理解能力。 Qwen-VL采用了三阶段的训练流程,并在多个视觉语言理解基准测试中取得了领先的成绩。该模型支持多语言、多图…

这一次,阿里能完成变革么

更多精彩内容在公众号。 马云在阿里内网发表题为《致改革 致创新》的帖子。释放支持继续改革信号。全文参考下图 马云在最近的发言中首先引用了阿里巴巴集团董事局主席蔡崇信的一次采访&#xff0c;表示对蔡崇信坦率地承认过去错误的勇敢态度表示赞赏。马云强调&#xff0c;犯错…

【MapBox】实现实时飞行轨迹功能

之前写了一篇MapBox添加带箭头的轨迹线&#xff0c;现在在这个基础之上实现获取到无人机的推送点位数据实时飞行的功能 首先创建实例&#xff0c;将无人机的图标加载在地图上 const MAP_UAV_FLIGHT_ING (values, layerKey 无人机飞行) > {ClearUAVMap();const map GET_…

功能测试_验证新浪邮箱登录的正确性

案例&#xff1a;验证验证新浪邮箱登录的正确性 功能测试_等价类设计用例&#xff1a; 步骤&#xff1a; 1:明确需求&#xff1a;邮箱能否登录 2:划分等价类&#xff1a;有效等价类、有效取值、无效等价类、无效取值 3&#xff1a;提取数据编写用例&#xff1a;用例编号、…