@Transactional导致数据库连接数不够
在Spring中进行事务管理非常简单,只需要在方法上加上注解@Transactional,Spring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务@Transactional划上了等号,只要有数据库相关操作就直接给方法加上@Transactional注解。
-
/** -
* 代码片段1 -
*/ -
@Transactional(rollbackFor = Exception.class) -
public void save(RequestBillDTO requestBillDTO){ -
//调用流程HTTP接口创建工作流 -
workflowUtil.createFlow("BILL",requestBillDTO); -
//转换DTO对象 -
RequestBill requestBill = JkMappingUtils.convert(requestBillDTO, RequestBill.class); -
requestBillDao.save(requestBill); -
//保存明细表 -
requestDetailDao.save(requestBill.getDetail()) -
}
先通过http接口调用工作流引擎创建审批流,然后保存,而为了保证 操作的事务,在整个方法上加上了@Transactional注解(仔细想想,这样真的能保证事务吗? )。
代码发布上线后,系统开始出现了故障:数据库监控平台一直收到告警短信,数据库连接不足,出现大量死锁;日志显示调用流程引擎接口出现大量超时;同时一直提示CannotGetJdbcConnectionException,数据库连接池连接占满。在发生故障后,我们尝试过杀掉死锁进程,也进行过暴力重启,只是不到10分钟故障再次出现。
我们知道@Transactional 注解,是使用 AOP 实现的,本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务。
当 Spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,对于@Transactional注解包裹的整个方法都是使用同一个connection连接 。如果我们出现了耗时的操作,比如第三方接口调用,业务逻辑复杂,大批量数据处理等就会导致我们我们占用这个connection的时间会很长,数据库连接一直被占用不释放。一旦类似操作过多,就会导致数据库连接池耗尽。
在一个事务中执行RPC操作导致数据库连接池撑爆属于是典型的长事务问题 ,类似的操作还有在事务中进行大量数据查询,业务规则处理等...
何为长事务?
顾名思义就是运行时间比较长,长时间未提交的事务,也可以称之为大事务 。
长事务会引发哪些问题?
长事务引发的常见危害有:
-
数据库连接池被占满,应用无法获取连接资源;
-
容易引发数据库死锁;
-
数据库回滚时间长;
-
在主从架构中会导致主从延时变大。
如何避免长事务?
解决长事务的宗旨就是 对事务方法进行拆分,尽量让事务变小,变快,减小事务的颗粒度。
既然提到了事务的颗粒度,我们就先回顾一下Spring进行事务管理的方式。
声明式事务
首先我们要知道,通过在方法上使用@Transactional注解进行事务管理的操作叫声明式事务 。
使用声明式事务的优点 很明显,就是使用很简单,可以自动帮我们进行事务的开启、提交以及回滚等操作。使用这种方式,程序员只需要关注业务逻辑就可以了。
声明式事务有一个最大的缺点 ,就是事务的颗粒度是整个方法,无法进行精细化控制。
与声明式事务对应的就是编程式事务 。基于底层的API,开发者在代码中手动的管理事务的开启、提交、回滚等操作。在spring项目中可以使用TransactionTemplate类的对象,手动控制事务。
-
private TransactionTemplate transactionTemplate; -
... -
public void save(RequestBill requestBill) { -
transactionTemplate.execute(transactionStatus -> { -
requestBillDao.save(requestBill); -
//保存明细表 -
requestDetailDao.save(requestBill.getDetail()); -
return Boolean.TRUE; -
}); -
}
使用编程式事务最大的好处就是可以精细化控制事务范围。
所以避免长事务最简单的方法就是不要使用声明式事务@Transactional,而是使用编程式事务手动控制事务范围。
有的同学会说,@Transactional使用这么简单,有没有办法既可以使用@Transactional,又能避免产生长事务?
那就需要对方法进行拆分,将不需要事务管理的逻辑与事务操作分开:
-
@Service -
public class OrderService{ -
public void createOrder(OrderCreateDTO createDTO){ -
query(); -
validate(); -
saveData(createDTO); -
} -
//事务操作 -
@Transactional(rollbackFor = Throwable.class) -
public void saveData(OrderCreateDTO createDTO){ -
orderDao.insert(createDTO); -
} -
}
query()与validate()不需要事务,我们将其与事务方法saveData()拆开。
当然,这种拆分会命中使用@Transactional注解时事务不生效的经典场景,很多新手非常容易犯这个错误。@Transactional注解的声明式事务是通过spring aop起作用的,而spring aop需要生成代理对象,直接在同一个类中方法调用使用的还是原始对象,事务不生效。其他几个常见的事务不生效的场景为:
“
@Transactional 应用在非 public 修饰的方法上
@Transactional 注解属性 propagation 设置错误
@Transactional 注解属性 rollbackFor 设置错误
同一个类中方法调用,导致@Transactional失效
异常被catch捕获导致@Transactional失效
”
正确的拆分方法应该使用下面两种:
-
可以将方法放入另一个类,如新增
manager层,通过spring注入,这样符合了在对象之间调用的条件。-
@Service -
public class OrderService{ -
@Autowired -
private OrderManager orderManager; -
public void createOrder(OrderCreateDTO createDTO){ -
query(); -
validate(); -
orderManager.saveData(createDTO); -
} -
} -
@Service -
public class OrderManager{ -
@Autowired -
private OrderDao orderDao; -
@Transactional(rollbackFor = Throwable.class) -
public void saveData(OrderCreateDTO createDTO){ -
orderDao.saveData(createDTO); -
} -
}
-
-
启动类添加
@EnableAspectJAutoProxy(exposeProxy = true),方法内使用AopContext.currentProxy()获得代理类,使用事务。-
SpringBootApplication.java -
@EnableAspectJAutoProxy(exposeProxy = true) -
@SpringBootApplication -
public class SpringBootApplication {} -
OrderService.java -
public void createOrder(OrderCreateDTO createDTO){ -
OrderService orderService = (OrderService)AopContext.currentProxy(); -
orderService.saveData(createDTO); -
}
小结
使用
@Transactional注解在开发时确实很方便,但是稍微不注意就可能出现长事务问题。所以对于复杂业务逻辑,我这里更建议你使用编程式事务来管理事务,当然,如果你非要使用@Transactional,可以根据上文提到的两种方案进行方法拆分。 -
相关文章:
@Transactional导致数据库连接数不够
在Spring中进行事务管理非常简单,只需要在方法上加上注解Transactional,Spring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务Transactional划上了等号,只要有数据库相关操作就直接给方法加上Transactiona…...
python3中的string 和bytes有什么区别
在Python中,string(字符串)和bytes(字节序列)是两种不同的数据类型,分别用于表示文本和二进制数据。它们的主要区别在于存储的数据类型、编码方式以及使用场景。 1. 存储数据类型 string (字符串,str):用来表示文本数据。string是一个Unicode字符串,其中的每个字符是…...
C~排序算法
在C/C中,有多种排序算法可供选择,每种算法都有其特定的应用场景和特点。下面介绍几种常用的排序算法,包括冒泡排序、选择排序、插入排序、快速排序、归并排序和堆排序,并给出相应的示例代码和解释。 冒泡排序(Bubble …...
基于github创建个人主页
基于github创建个人主页 站在巨人的肩膀上,首先选一个创建主页的仓库进行fork,具体可以参照这篇文章https://blog.csdn.net/qd1813100174/article/details/128604858主要总结下需要修改的地方: 1)仓库名字要和github的名字一致&a…...
apt update时出现证书相关问题,可以关闭apt验证
vi /etc/apt/apt.conf.d/99disable-signature-verification 添加以下内容: Acquire::AllowInsecureRepositories "true"; Acquire::AllowDowngradeToInsecureRepositories "true"; Acquire::AllowUnauthenticated "true"; 参考链…...
进阶数据库系列(十三):PostgreSQL 分区分表
概述 在组件开发迭代的过程中,随着使用时间的增加,数据库中的数据量也不断增加,因此数据库查询越来越慢。 通常加速数据库的方法很多,如添加特定的索引,将日志目录换到单独的磁盘分区,调整数据库引擎的参…...
翻译:Recent Event Camera Innovations: A Survey
摘要 基于事件的视觉受到人类视觉系统的启发,提供了变革性的功能,例如低延迟、高动态范围和降低功耗。本文对事件相机进行了全面的调查,并追溯了事件相机的发展历程。它介绍了事件相机的基本原理,将其与传统的帧相机进行了比较&am…...
车载诊断技术:汽车健康的守护者
一、车载诊断技术的发展历程 从最初简单的硬件设备到如今智能化、网络化的系统,车载诊断技术不断演进,为汽车安全和性能提供保障。 早期的汽车诊断检测技术处于比较原始的状态,主要依靠操作经验和主观评价。随着汽车工业的发展,车载诊断技术也经历了不同的阶段。20 世纪初…...
“天翼云息壤杯”高校AI大赛开启:国云的一场“造林”计划
文 | 智能相对论 作者 | 叶远风 2024年年初《政府工作报告》中明确提到了“人工智能”行动,人工智能的发展被提到前所未有的高度。 如何落实AI在数字经济发展中引擎作用,是业界当下面临的课题。 9月25日,“2024年中国国际信息通信展览会”…...
【怎样基于Okhttp3来实现各种各样的远程调用,表单、JSON、文件、文件流等待】
HTTP客户端工具 okhttp3 form/json/multipart 提供表达、json、混合表单、混合表单文件流传输等HTTP请求调用支持自定义配置默认客户端,参数列表如下: okhtt3.config.connectTimeout 连接超时,TimeUnit.SECONDSokhtt3.config.readTimeOut 读…...
excel统计分析(3): 一元线性回归分析
简介 用途:研究两个具有线性关系的变量之间的关系。 一元线性回归分析模型: ab参数由公式可得: 判定系数R2:评估回归模型的拟合效果。值越接近1,说明拟合效果越好;值越接近0,说明拟合效果越…...
搜索引擎onesearch3实现解释和升级到Elasticsearch v8系列(一)-概述
简介 此前的专栏介绍onesearch1.0和2.0,详情参看4 参考资料,本文解释onesearch 3.0,从Elasticsearch6升级到Elasticsearch8代码实现 ,Elasticsearch8 废弃了high rest client,使用新的ElasticsearchClient,…...
ArcGIS Pro高级地图可视化—双变量符号地图
ArcGIS Pro高级地图可视化 ——双变量符号地图 1 背景 “我不是双变量,但我很好奇。”出自2013 年南卡罗来纳州格林维尔举行的 NACIS 会议上,双变量地图随着这句俏皮的话便跳跃在人们的视角下,在讨论二元映射之后,它不仅恰逢其…...
rust属性宏
1. #[repr(xxx)] repr全称是 “representation”,即表示、展现的意思。在#[repr(u32)]中,u32表示无符号 32 位整数。这意味着被这个属性修饰的类型将以 32 位无符号整数的形式在内存中存储和布局。例如,如果有一个枚举类型被#[repr(u32)]修饰: #[repr(u32)] enum MyEnum {…...
《pyqt+open3d》open3d可视化界面集成到qt中
《pyqtopen3d》open3d可视化界面集成到qt中 一、效果显示二、代码三、资源下载 一、效果显示 二、代码 参考链接 main.py import sys import open3d as o3d from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QWindow from PyQt5.Qt…...
学习记录:js算法(四十七):相同的树
文章目录 相同的树我的思路网上思路队列序列化方法 总结 相同的树 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 图一: 图二&…...
使用Hutool-poi封装Apache POI进行Excel的上传与下载
介绍 Hutool-poi是针对Apache POI的封装,因此需要用户自行引入POI库,Hutool默认不引入。到目前为止,Hutool-poi支持: Excel文件(xls, xlsx)的读取(ExcelReader)Excel文件(xls&…...
asp.net core grpc快速入门
环境 .net 8 vs2022 创建 gRPC 服务器 一定要勾选Https 安装Nuget包 <PackageReference Include"Google.Protobuf" Version"3.28.2" /> <PackageReference Include"Grpc.AspNetCore" Version"2.66.0" /> <PackageR…...
拿到一个新项目,如何开展测试
1. 拿到一个新的项目或者新的需求,首先需要搞清楚他的背景、目标和需求,这个过程需要和产品、开发、客户去沟通。 2. 清楚需求后,首先将业务流程走通,确保项目的基础功能是正常的 3. 根据项目需求明确测试的目标,如&…...
pre-commit 的配置文件
这个文件是 pre-commit 的配置文件,通常命名为 .pre-commit-config.yaml。pre-commit 是一个用于管理和维护多种预提交钩子的框架,旨在在代码提交(git commit)之前自动执行一系列检查和格式化任务,以确保代码质量和一致…...
编译原理避坑指南:自顶向下语法分析的5个常见错误及解决方法
编译原理避坑指南:自顶向下语法分析的5个常见错误及解决方法 第一次接触自顶向下语法分析时,我盯着那个无限循环的递归文法整整三天没想明白——为什么明明按照教材步骤操作,程序却始终报错?直到助教指出我忽略了间接左递归的隐蔽…...
GitHub中文界面终极指南:5分钟让你的GitHub说中文
GitHub中文界面终极指南:5分钟让你的GitHub说中文 【免费下载链接】github-chinese GitHub 汉化插件,GitHub 中文化界面。 (GitHub Translation To Chinese) 项目地址: https://gitcode.com/gh_mirrors/gi/github-chinese 想象一下,你…...
22:L应用区块链+AI:蓝队的分布式安全
作者: HOS(安全风信子) 日期: 2026-03-19 主要来源平台: GitHub 摘要: 区块链的不可篡改特性与AI的智能分析能力相结合,为蓝队防御带来了新的可能性。L深入研究区块链AI的融合应用,构建了一个分布式、透明、…...
从CISC到RISC:指令寻址方式如何影响CPU设计?
从CISC到RISC:指令寻址方式如何重塑现代CPU设计? 在计算机体系结构的演进历程中,指令寻址方式始终是影响处理器性能的关键因素。当我们比较x86与ARM处理器的能效差异时,或是分析苹果M系列芯片为何能在低功耗下实现惊人性能时&…...
Kettle错误处理实战:如何用表输出步骤捕获并存储ETL过程中的异常数据
Kettle错误处理实战:如何用表输出步骤捕获并存储ETL过程中的异常数据 在数据仓库和ETL(Extract, Transform, Load)流程中,错误处理是确保数据质量的关键环节。Kettle(现称Pentaho Data Integration)作为一款…...
洛谷 P1833:樱花 ← 混合背包(01 + 完全 + 多重)
【题目来源】 https://www.luogu.com.cn/problem/P1833 【题目描述】 爱与愁大神后院里种了 n 棵樱花树,每棵都有美学值 Ci(0<Ci≤200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍…...
Java面试如何突击?核心知识点有哪些?该如何准备拿下offer?
一、Java 面试核心知识点(按考察优先级排序)1. Java 基础面向对象:封装、继承、多态(重载与重写)、抽象类与接口的区别。String 系列:String 不可变性、StringBuilder 与 StringBuffer 的区别、常量池。集合…...
打破学术写作边界:NativeOverleaf离线工作流全解析
打破学术写作边界:NativeOverleaf离线工作流全解析 【免费下载链接】NativeOverleaf Next-level academia! Repository for the Native Overleaf project, attempting to integrate Overleaf with native OS features for macOS, Linux and Windows. 项目地址: ht…...
STM32串口环形队列IAP固件更新方案
基于STM32串口环形队列的IAP实现方案1. 项目概述1.1 系统架构本方案实现了一种基于STM32F103C8T6微控制器的串口IAP(In-Application Programming)系统,采用环形队列缓冲机制解决有限SRAM空间下的固件更新问题。系统将64KB Flash空间划分为四个功能区域:B…...
六边形地理索引的终极指南:H3算法如何革新空间数据分析
六边形地理索引的终极指南:H3算法如何革新空间数据分析 【免费下载链接】h3 Hexagonal hierarchical geospatial indexing system 项目地址: https://gitcode.com/gh_mirrors/h3/h3 你是否曾为处理大规模地理空间数据而头疼?传统的地理索引系统在…...
