spring重试机制

news2025/6/7 21:14:35

数据库死锁处理与重试机制实现指南

1. 业务场景

1.1 问题现象

  • 高并发批量数据处理时频繁出现数据库死锁
  • 主要发生在"先删除历史数据,再重新计算"的业务流程中
  • 原有逐条处理方式:list.forEach(item -> { delete(); calculate(); })

1.2 死锁原因分析

  • 锁竞争:多个线程同时对相同数据进行删除和插入操作
  • 事务时间过长:删除和计算在同一事务中,持锁时间长
  • 锁升级:行锁升级为表锁,增加死锁概率

2. 改造步骤

2.1 添加依赖

pom.xml 中添加Spring Retry相关依赖:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

2.2 创建重试配置类

@Configuration
@EnableRetry
public class RetryConfig {
    
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        
        // 重试策略:最多重试3次
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        retryTemplate.setRetryPolicy(retryPolicy);
        
        // 退避策略:指数退避,初始延迟1秒
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000);
        backOffPolicy.setMultiplier(2.0);
        backOffPolicy.setMaxInterval(5000);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        
        return retryTemplate;
    }
}

2.3 启用重试机制

在主应用类上添加 @EnableRetry 注解:

@EnableRetry
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.4 改造核心处理逻辑

改造前(容易死锁):
public void processData(List<DataDto> list, QueryParam param) {
    // 逐条处理:删除 + 计算
    list.forEach(item -> {
        deleteRelatedData(item.getId());  // 单条删除
        calculateData(item);              // 单条计算
    });
}
改造后(两阶段处理):
public void processData(List<DataDto> list, QueryParam param) {
    if (CollectionUtils.isEmpty(list)) {
        return;
    }
    
    // 第一阶段:范围删除(有重试机制)
    deleteDataByRange(param.getType(), param.getCategory(), 
                     param.getStartTime(), param.getEndTime(), 
                     param.getTempId());
    
    // 第二阶段:批量计算(纯计算,无删除操作)
    for (DataDto item : list) {
        calculateData(item);
    }
}

2.5 在关键方法上添加重试注解

@Retryable(
    value = {DeadlockLoserDataAccessException.class, DataAccessException.class},
    maxAttempts = 3,
    backoff = @Backoff(delay = 1000, multiplier = 2, maxDelay = 5000)
)
public void deleteDataByRange(String type, String category, 
    LocalDateTime startTime, LocalDateTime endTime, String tempId) {
    
    log.info("开始范围删除数据:type={}, category={}", type, category);
    dataMapper.deleteByRange(type, category, startTime, endTime, tempId);
    log.info("范围删除完成");
}

@Recover
public void recoverFromDeadlock(Exception ex, String type, String category,
    LocalDateTime startTime, LocalDateTime endTime, String tempId) {
    
    log.error("删除数据重试失败,最终放弃。参数:type={}, category={}", 
              type, category, ex);
    throw new BusinessException("数据删除失败,请稍后重试");
}

2.6 移除冗余删除操作

检查并移除业务流程中的冗余删除调用:

public void businessProcess(ProcessParam param) {
    // 移除冗余的删除调用
    // deleteRelatedData(param);  // 删除这行
    
    // 保留必要的删除操作
    deleteSpecificTypeData(param);
    
    // 业务计算逻辑
    processBusinessLogic(param);
}

3. 关键改造点总结

3.1 核心改造思路

  1. 分离删除和计算:避免在同一循环中进行删除和计算
  2. 范围删除替代逐条删除:减少数据库操作次数和锁竞争
  3. 添加重试机制:对不可避免的死锁进行自动重试
  4. 清理冗余操作:移除不必要的删除调用

3.2 改造前后对比

改造前改造后
逐条删除 + 计算范围删除 + 批量计算
长事务持锁短事务快速释放锁
无重试机制自动重试死锁异常
多处冗余删除精简删除操作

3.3 效果验证

  • 死锁发生频率显著降低
  • 数据处理性能提升
  • 系统稳定性增强
  • 无数据丢失问题

4. 注意事项

4.1 重试配置要点

  • 只对特定异常类型重试(如死锁异常)
  • 设置合理的重试次数和间隔
  • 必须提供 @Recover 方法处理最终失败

4.2 两阶段处理要点

  • 确保删除和计算之间没有其他操作干扰
  • 删除操作要支持范围查询
  • 计算逻辑要保证幂等性

4.3 数据一致性保证

  • 关键保存操作不能遗漏
  • 事务边界要合理设置
  • 必要时使用分布式锁

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

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

相关文章

智能制造数字孪生全要素交付一张网:智造中枢,孪生领航,共建智造生态共同体

在制造业转型升级的浪潮中&#xff0c;数字孪生技术正成为推动行业变革的核心引擎。从特斯拉通过数字孪生体实现车辆全生命周期优化&#xff0c;到海尔卡奥斯工业互联网平台赋能千行百业&#xff0c;数字孪生技术已从概念验证走向规模化落地。通过构建覆盖全国的交付网络&#…

stylus - 新生代CSS预处理框架

stylus是什么 Stylus 是一种 CSS 预处理器&#xff0c;它扩展了 CSS 的功能&#xff0c;使得编写样式变得更简洁和高效。Stylus 允许使用嵌套、变量、混入等编程功能&#xff0c;这些功能可以极大地提高开发效率和代码的可维护性。 stylus中文文档 https://stylus.uihtm.co…

BugKu Web渗透之网站被hei(仅仅是ctf题目名称)

启动场景&#xff0c;打开网页&#xff0c;显示如下&#xff1a; 目前没有看出任何异常。 步骤一&#xff1a; 右键查看源代码。源代码较多&#xff0c;也没发现异常。 步骤二&#xff1a; 用dirsearch扫描网站目录。 如图&#xff1a; 看起来shell.php很可疑。 步骤三&…

关于华为仓颉编程语言

文章目录 一、基本概况二、技术特点1. 多范式编程2. 原生智能化3. 高性能与安全4. 全场景兼容 三、编译器与开发工具四、语言相似性对比五、行业应用实例总结 最近经常看到这个东西&#xff0c;于是搜了一下&#xff0c;整理了一些内容&#xff0c;水一篇&#xff0c;以后慢慢研…

解决el-select选择框右侧下拉箭头遮挡文字问题

如图所示&#xff1a; el-select长度较短的时候&#xff0c;选择框右侧下拉箭头会遮挡选中的数据 选中数据被遮挡 解决办法&#xff1a; 组件如下&#xff1a; <td class"fmtd" :colspan"col.ptproCupNum" v-for"col in row" :key"…

20250603在荣品的PRO-RK3566开发板的Android13下的使用命令行来查看RK3566的温度【显示优化版本】

20250603在荣品的PRO-RK3566开发板的Android13下的使用命令行来查看RK3566的温度【显示优化版本】 2025/6/3 11:58 RK3566的cpu运行效率 top busybox top rk3566_t:/ # rk3566_t:/ # rk3566_t:/ # cd /sys/class/thermal/ rk3566_t:/sys/class/thermal # ls -l rk3566_t:/sys/c…

C语言字符数组初始化的5种方法(附带实例)

所谓初始化&#xff0c;就是在定义的同时进行赋值。 C语言中&#xff0c;初始化字符数组的方式多样&#xff0c;每种方式都有其特定的用途和优势。 1、使用字符串字面量初始化 最常见和简洁的初始化方式是使用字符串字面量。在这种方法中&#xff0c;我们直接将一个用双引号…

npm run dev 报错:Error: error:0308010C:digital envelope routines::unsupported

npm run dev时报错如下 原因&#xff1a;更换node版本导致 解决&#xff1a; 修改package.json文件&#xff0c;在相关构建命令之前加入 SET NODE_OPTIONS–openssl-legacy-provider 运行成功

基于LLaMA-Factory和Easy Dataset的Qwen3微调实战:从数据准备到LoRA微调推理评估的全流程指南

随着开源大模型如 LLaMA、Qwen 和 Baichuan 的广泛应用&#xff0c;其基于通用数据的训练方式在特定下游任务和垂直领域中的表现仍存在提升空间&#xff0c;因此衍生出针对具体场景的微调训练需求。这些训练涵盖预训练&#xff08;PT&#xff09;、指令微调&#xff08;SFT&…

idea中 maven 本地仓库有jar包,但还是找不到,解决打包失败和无法引用的问题

1、删除本地仓库中的文件 进入本地仓库对应jar包文件目录中删除_remote.repositories文件和结尾为.lastUpdated的文件 2、回到IDEA刷新Maven 3、查看之前引用不了的jar是否引入成功

(33)课54--??:3 张表的 join-on 连接举例,多表查询总结。

&#xff08;112&#xff09;3 张表的 join-on 连接举例 &#xff1a; &#xff08;113&#xff09; 多表查询总结 &#xff1a; &#xff08;114&#xff09;事务 &#xff1a; &#xff08;115&#xff09; &#xff08;116&#xff09; &#xff08;117&#xff09; …

SpringCloud学习笔记-3

声明&#xff1a;笔记来源于网络&#xff0c;如有侵权联系删除 1 openfeign 1&#xff09;openfeign远程调用声明式实现 1.启动类中添加注解 EnableFeignClients EnableFeignClients SpringBootApplication public class OrderMainApplication {public static void main(St…

BugKu Web渗透之eval

启动场景&#xff0c;打开网页&#xff0c;显示的是一段代码。 步骤一&#xff1a; 分析代码。 代码大概意思是&#xff1a; <?php//包含"flag.php"的文件include "flag.php"; //获取网页请求的hello数据$a $_REQUEST[hello]; //显示变量a的详…

DAY45 可视化

DAY 45 Tensorborad 之前的内容中&#xff0c;我们在神经网络训练中&#xff0c;为了帮助自己理解&#xff0c;借用了很多的组件&#xff0c;比如训练进度条、可视化的loss下降曲线、权重分布图&#xff0c;运行结束后还可以查看单张图的推理效果。 如果现在有一个交互工具可…

11.RV1126-ROCKX项目 API和人脸检测画框

一.ROCKX的API 1.ROCKX的作用 ROCKX的AI组件可以快速搭建 AI的应用&#xff0c;这些应用可以是车牌识别、人脸识别、目标识别&#xff0c;人体骨骼识别等等。主要用于各种检测识别。例如下图&#xff1a; 2.ROCKX人脸识别的API rockx_ret_t rockx_create(rockx_handle_t *han…

超构光学与 AR 的深度融合 | 攻克 VAC 与眼动范围难题

原文信息 原文标题&#xff1a;“Three-dimensional varifocal meta-device for augmented reality display” 第一作者&#xff1a;宋昱舟&#xff0c;袁家琪&#xff0c;陳欽杪&#xff0c;刘小源 &#xff0c;周寅&#xff0c;程家洛&#xff0c;肖淑敏*&#xff0c;陈沐…

[ Qt ] | 与系统相关的操作(三):QFile介绍和使用

目录 之前的操作文件的方式 Qt中的文件操作简介 QFile 打开 读 写 关闭 一个例子来说明 QFileInfo 之前的操作文件的方式 C语言中&#xff0c;fopen 打开文件&#xff0c;fread fwrite 读写文件&#xff0c;fclose 关闭文件。 C中&#xff0c;fstream 打开文件&…

软件工程:如何做好软件产品

1、什么是产品 从项目到产品 产品&#xff1a;满足行业共性需求的标准产品。即要能够做到配置化的开发&#xff0c;用同一款产品最大限度地满足不同客户的需求&#xff0c;同时让产品具有可以快速响应客户需求变化的能力。 好的产品一定吸收了多个项目的共性&#xff0c;一定是…

蓝桥杯 省赛 2025python(B组)题目(分析)

目录 第一题 为什么答案是103而不是104&#xff1f; 第二题 为什么必须按长度排序&#xff1f; 第三题 易错点总结 第四题 逻辑问题&#xff1a; 可能超过时间复杂度的代码示例 1. 暴力枚举所有可能的子串 2. 递归回溯 第五题 1. 暴力枚举法 2. 优化枚举 3.数…

React - 组件通信

组件通信 概念&#xff1a;组件通信就是组件之间数据传递&#xff0c;根据组件嵌套关系不同&#xff0c;有不同的通信方法 父传子 —— 基础实现 实现步骤 父组件传递数据 - 在子组件标签上绑定属性子组件接收数据 - 子组件通过props参数接收数据 声明子组件并使用 //声明子…