记录一次Spring事务失效导致的生产问题

一、背景介绍

公司做的是“聚合支付”业务,对接了微信、和包、数字人民币等等多家支付机构,我们提供统一的支付、退款、自动扣款签约、解约等能力给全国的省公司、机构、商户等。

同时,需要做对账功能,即支付机构将对账文件给到我们聚合支付,由我们进行如下三个定时任务:

1、入库:解析支付机构给的对账文件,解析每一笔订单,登记到数据库中;

2、对账:将支付机构的对账数据,与我们本地的对账数据,进行比对;

3、生成文件并下发:对完后,将对完账的我们的数据,按商户生成文件,并下发给商户的sftp机器。

我们的问题,就出在这个对账上面。

我们每天数据量都有500万-2000万笔订单数据,导致我们入库时,解析文件,每2000条数据提交数据库一次,压力还算能够承受;但是由于集团业务调整,导致我们每天的订单量,激增20%-50%。

而月初月末,交易量会更大,所以一到月末月初那阵子,每天的数据量都在两千万以上

这时由于支付机构给的对账数据更多,导致入库时间长,数据库DBA也提醒我们数据库压力大,所以我们研讨决定,牺牲部分时间,提升稳定性;将每2000条提交一次,修改为1000条修改一次;

导致入库的时间翻倍。这是业务背景

二、出现的异常现象

月中14号,修改了这个参数(2000笔提交一次->1000笔提交一次),一切正常。

到了月末(我们的业务月初、月末是高峰期,数据量更多),问题就来了:

我们的三个定时任务中的对账任务,对账突然卡住了,一直都没有动弹,出现告警;

由于一时看不出问题,临时决定使用:

重启大法。

重启后,并手动删除分布式锁后,对账任务恢复正常并下发对账文件给商户。

三、排查问题

前期排查,数据库资源cpu达到100%,以为是数据库的性能限制,导致对账线程池出现异常,无法获取连接导致的;但是后续我们调整多线程对账的线程数(10线程->8线程),数据库CPU占用降到90%多,但是第二天依然出现卡死现象。

无奈,继续排查:

通过使用jmap和jstack命令

# 打印 20250305.heapdump.hprof
# 这个命令用于生成Java进程的堆内存转储文件(Heap Dump),便于后续分析内存问题(如内存泄漏)
jmap -dump:live,format=b,file=20250305.heapdump.hprof  2365589# 打印 20250305.threaddump.txt
# 这个命令用于生成Java进程的线程转储(Thread Dump),帮助诊断线程阻塞、死锁、高CPU占用等问题。
jstack -l 2365589> 20250305.threaddump.txt

线程转储中看到,对数据库中有一个表的操作没有得到响应,就一直卡住

问了DBA的人员,确定,是这张表的行锁引起了问题。

于是,我们去看代码分析,经过我的仔细观察,与推翻各种不成立的假设后,终于被我定位到一个方法调用的错误:

如下是我们的大致调用图

可以看到解析并入库的主方法A,是带事务Transactional的

在下载文件完成后,调用方法B,再调用方法C,修改了一张状态表tb_status,修改状态为"下载成功"。

这里本意是方法C新开一个事务(REQUIRES_NEW),这样修改完后,单独提交;

后面的方法E,也是同样的打算。

可是写这段代码的时候没想到,spring事务注解是有可能会失效的;

在 Spring 中,当一个类中的方法 自调用(即类内部方法A调用方法B)时,如果方法B上标注了 @Transactional 事务注解,事务会失效。这是由 Spring 的 AOP 代理机制导致的。

在我们这个案例中,正是出现了这样的情况:方法A,调用B,再调用C时,以为C会新开一个事务去直接提交,但是没想到事务失效了,事务C的修改,并没有直接提交;

一直等到,文件解析入库(耗时很长)后,方法A的整个流程结束,才一起提交;

导致这一段时间内,定时任务“对账”想修改这一行数据,无法修改,导致卡住。

为什么之前没事呢?这代码跑了很久都没事啊,怎么回事呢?这就要结合我前面罗里吧嗦说的一大堆业务来看了,本来因为集团切业务,导致我们系统业务量暴增,又是月初月末,数据量更加大;光是一个微信商户给的对账文件就有2.5GB多(900多万,接近一千万订单);

导致解析文件并入库时间由10分钟左右,增加到45分钟以上。我们的“对账”定时任务,每15分钟执行一次,而入库的这45分钟,由于对tb_status的修改,一直没有提交,故而“对账”任务,意图修改tb_status的状态时,一直拿不到行锁修改数据,就卡住了。

四、解决方案

既然知道问题的原因,那么解决方案,我相信读者有很多种。

第一:方法A不带事务,让其他自己的事务,各自以非事务的方式,自动提交(此方式一定要根据自己业务逻辑的上下文来看,不能盲目各自提交)

第二:修改方法C上注解的位置,移动至方法B使其生效

经过评估,我们是选择了第一种。

另外,附上spring事务失效的几种场景:

1、如果被@Transactional修饰的方法,不是public的,那么事务会失效;

2、一个类中的方法 自调用(即类内部方法A调用方法B)时,如果方法B上标注了 @Transactional 事务注解,事务会失效。

3、如果一个方法是final的,那么加@Transactional事务也是无法生效的

4、被try-catch捕获了异常,没有往外抛出,那么spring事务会认为方法,没有发生异常,就不会回滚,事务失效

5、数据库表本身不支持事务,导致事务失效,例如InnoDB就不支持事务

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IntelliJ IDEA集成MarsCode AI

IntelliJ IDEA集成MarsCode AI IDEA中安装插件 安装完毕之后登录自己的账号 点击链接,注册账号 https://www.marscode.cn/events/s/i5DRGqqo/ 可以选择不同的模型

FPGA学习篇——Verilog学习4

1.1 结构语句 结构语句主要是initial语句和always语句,initial 语句它在模块中只执行一次,而always语句则不断重复执行,以下是一个比较好解释的图: (图片来源于知乎博主罗成,画的很好很直观!) 1.1.1 initial语句 ini…

并发编程(线程池)面试题及原理

1. 执行原理/核心参数 1.1 核心参数 核心参数 corePoolSize 核心线程数目maximumPooISize 最大线程数目 (核心线程+救急线程的最大数目)keepAliveTime 生存时间- 救急线程的生存时间,生存时间内没有新任务,此线程资…

Java 大视界 -- Java 大数据在智慧交通信号灯智能控制中的应用(116)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…

1.15-16-17-18迭代器与生成器,函数,数据结构,模块

目录 15,Python3 迭代器与生成器15-1 迭代器15-1-1 基础知识15-1-2 迭代器与for循环工作原理 15-2 生成器(本质就是迭代器)15-2-1 yield 表达式15-2-2 三元表达式15-2-3 列表生成式15-2-4 其他生成器(——没有元祖生成式——&…

如何同步this.goodAllData里面的每一项给到row

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…

react中如何使用使用react-redux进行数据管理

以上就是react-redux的使用过程,下面我们开始优化部分:当一个组件只有一个render生命周期,那么我们可以改写成一个无状态组件(UI组件到无状态组件,性能提升更好)

基于qt的桌面宠物——纳西妲源码纯享

本专栏会不定时更新&#xff0c;如果有有趣的C代码或者编程可以在评论区留言&#xff0c;我会尽量满足粉丝的要求&#xff0c;同时还希望看到的朋友点个赞/收藏&#xff08;感谢/感谢&#xff09; 代码 main.cpp: #include "widget.h"#include <QApplication&g…

用于管理 Elasticsearch Serverless 项目的 AI Agent

作者&#xff1a;来自 Elastic Fram Souza 由自然语言驱动的 AI 代理&#xff0c;可轻松管理 Elasticsearch Serverless 项目 - 支持项目创建、删除和状态检查。 这个小型命令行工具让你可以用简单的英语管理你的无服务器 Elasticsearch 项目。它通过AI&#xff08;这里是 Ope…

C语言_图书管理系统_借阅系统管理

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 小伞的主页&#xff1a;xiaosan_blog 本文所需对顺序表的理解&#xff1a; 注&#xff1a;由于顺序表实现图书…

车辆运维管理行业洞察与竞品分析

1. 前言 车辆运维管理是指对车辆进行日常维护、故障处理、性能监测、成本控制等一系列活动的管理。随着物联网、大数据、人工智能等技术的发展&#xff0c;车辆运维管理软件和解决方案的市场竞争日益激烈。 2. 确定目标 通过产品差异化定位&#xff0c;找到竞争者的差异&…

Spring源码分析のAOP

文章目录 前言一、wrapIfNecessary1.1、getAdvicesAndAdvisorsForBean1.1.1、findCandidateAdvisors1.1.2、findAdvisorsThatCanApply 1.2、createProxy 二、invoke2.1、getInterceptorsAndDynamicInterceptionAdvice2.1.1、getInterceptors 2.2、proceed2.2.1、invoke 三、Asp…

LINUX网络基础 [一] - 初识网络,理解网络协议

目录 前言 一. 计算机网络背景 1.1 发展历程 1.1.1 独立模式 1.1.2 网络互联 1.1.3 局域网LAN 1.1.4 广域网WAN 1.2 总结 二. "协议" 2.1 什么是协议 2.2 网络协议的理解 2.3 网络协议的分层结构 三. OSI七层模型&#xff08;理论标准&#xff09; …

React学习笔记10

一、Redux与React-提交action传参 需求&#xff1a;组件中有两个按钮&#xff0c;点击add to 10和add to 20将count的值修改到对应的数字&#xff0c;目标count值是在组件中传递过去的&#xff0c;需要提交action的时候传递参数 实现思路&#xff1a;在reducers的同步修改方法中…

Docker概念与架构

文章目录 概念docker与虚拟机的差异docker的作用docker容器虚拟化 与 传统虚拟机比较 Docker 架构 概念 Docker 是一个开源的应用容器引擎。诞生于 2013 年初&#xff0c;基于 Go 语言实现。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xf…

HarmonyOS Next~应用开发入门:从架构认知到HelloWorld实战

HarmonyOS应用开发入门&#xff1a;从架构认知到HelloWorld实战 一、HarmonyOS架构解析 1.1 分布式能力三要素 &#xff08;1&#xff09;软总线&#xff08;SoftBus&#xff09; HarmonyOS的核心神经中枢&#xff0c;通过统一的分布式通信协议实现设备间的自动发现和组网。…

相控阵扫盲

下图展示天线增益 在仰角为0度的情况下随着方位角的变化而变化。需要注意到的是在天线视轴方向上的高增益主瓣上还有几个低增益旁瓣 阵列因子乘以新的阵元方向图会形成指向性更强的波速

[QT]开发全解析:从概念到实战

文章目录 Qt 框架入门与应用开发指南一、Qt 框架概述1.1 什么是 Qt1.2 Qt 的发展史1.3 Qt 支持的平台1.4 Qt 版本1.5 Qt 的优点1.6 Qt 的应用场景1.7 Qt 的成功案例 二、Qt 的开发工具概述Qt CreatorVisual StudioEclipse 三、认识 Qt Creator3.1 Qt Creator 概览3.2 使用 Qt C…

LeetCode 718.最长重复子数组(动态规划,Python)

给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&#xff1a;长度最长的公共子数组是 [3,2,1] 。 示例 2&#xff1a; 输…

从厨电模范到数字先锋,看永洪科技如何助力方太集团开启数字新征程

在数字化洪流席卷全球的宏大背景下&#xff0c;企业转型升级的紧迫性与重要性日益凸显&#xff0c;成为驱动行业进步的关键引擎。在这一波澜壮阔的转型浪潮中&#xff0c;方太集团——厨电领域的璀璨明珠&#xff0c;以其前瞻性的战略视野和不懈的创新精神&#xff0c;携手数据…