Spring JPA deleteInBatch导致StackOverflow问题

 更新时间:2024年05月07日 09:01:01   作者:Mr-Wanter  
这篇文章主要介绍了Spring JPA deleteInBatch导致StackOverflow问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

数据库日志库定时清理日志数据,几天之后发现,jvm内存溢出现象。

一、问题定位

1、日志数据量太大(近40w数据)

2、源码分析

@Scheduled(cron = "0 0 1 * * ?")
	public void timedPullNewInfo() {
		Date date=new Date();
		Calendar ca=Calendar.getInstance();
		try{
			ca.setTime(date);
			ca.add(ca.DATE, -30);
			Date queryDate = ca.getTime();
			log.info("进入定时任务的方法中来清理前30天的操作日志================");
			List<GempUserOperationLogPO> polist = gempUserOperationLogDao.findByCreateTimeBefore(queryDate);
			gempUserOperationLogDao.deleteInBatch(polist);
		}catch (Exception e){
			log.info("清理前30天的操作日志出错================"+e.getMessage());
		}
	}

3、问题定位在

gempUserOperationLogDao.deleteInBatch(polist);

二、内存溢出原理

jpa封装的deleteInBatch底层执行逻辑为

delete from [table_name] where [criteria] = id or [criteria] = id (and so on...)

HqlSqlBaseWalker需要搜索遍历所有的where条件语句,当数据量过大时就会导致内存溢出。

三、其他尝试

1、循环遍历删除delete

List<GempUserOperationLogPO> polist = gempUserOperationLogDao.findByCreateTimeBefore(queryDate);
polist.forEach(x->{
	gempUserOperationLogDao.delete(x);
});

四十万数据等不起…

2、分批次删除

	//按每1000个一组分割
	private static final Integer MAX_NUMBER = 1000;

	/**
	 * 计算切分次数
	 */
	private static Integer countStep(Integer size) {
		return (size + MAX_NUMBER - 1) / MAX_NUMBER;
	}

	@Scheduled(cron = "0 0 1 * * ?")
	public void timedPullNewInfo() {
		Date date=new Date();
		Calendar ca=Calendar.getInstance();
		try{
			ca.setTime(date);
			ca.add(ca.DATE, -30);
			Date queryDate = ca.getTime();
			log.info("进入定时任务的方法中来清理前30天的操作日志================");

			List<GempUserOperationLogPO> polist = gempUserOperationLogDao.findByCreateTimeBefore(queryDate);
			int limit = countStep(polist.size());
			List<List<GempUserOperationLogPO>> mglist = new ArrayList<>();
			Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
				mglist.add(polist.stream().skip(i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toList()));
			});
			mglist.forEach(x->{
				gempUserOperationLogDao.deleteInBatch(x);
			});
		}catch (Exception e){
			log.info("清理前30天的操作日志出错================"+e.getMessage());
		}
	}

还是等不起…

3、直接全部删除

gempUserOperationLogDao.deleteAll();

deleteAll()底层执行逻辑:

  • 1、查询所有数据
  • 2、根据id逐一删除

与方式一没有区别,等到天荒地老

四、解决方案

原生sql删除,秒级方案

public interface GempUserOperationLogDao extends
        JpaRepository<GempUserOperationLogPO, String>, JpaSpecificationExecutor<GempUserOperationLogPO> {
        
    @Transactional
    @Modifying
    @Query(value = "delete from user_operation_log where create_time < ?1", nativeQuery = true)
    int deleteAllLists(Date date);
}

StopWatch sw = new StopWatch();
sw.start("校验耗时");
gempUserOperationLogDao.deleteAllLists(queryDate);
sw.stop();
System.out.println(sw.prettyPrint());
StopWatch '': running time (millis) = 943
ms%Task name
00943100%校验耗时

总结

面对大数据量的数据批量修改、删除操作时,不要使用jpa封装的方法操作数据,编写原生sql效率更高且不易发生问题。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Mybatis的核心配置文件使用方法

    Mybatis的核心配置文件使用方法

    Mybatis的核心配置文件有两个,一个是全局配置文件,它包含了会深深影响Mybatis行为的设置和属性信息;一个是映射文件,它很简单,让用户能更专注于SQL代码,本文主要介绍了Mybatis的核心配置文件使用方法,感兴趣的可以了解一下
    2023-11-11
  • Mybatis一对多和多对一处理的深入讲解

    Mybatis一对多和多对一处理的深入讲解

    Mybatis可以通过关联查询实现,关联查询是几个表联合查询,只查询一次,通过在resultMap里面的association,collection节点配置一对一,一对多的类就可以完成,这篇文章主要给大家介绍了关于Mybatis一对多和多对一处理的相关资料,需要的朋友可以参考下
    2021-09-09
  • Spring Boot循环依赖的症状和解决方案

    Spring Boot循环依赖的症状和解决方案

    循环依赖是指在Spring Boot 应用程序中,两个或多个类之间存在彼此依赖的情况,形成一个循环依赖链。这篇文章主要介绍了SpringBoot循环依赖的症状和解决方法
    2023-04-04
  • 一文详解如何使用Java来发送qq邮箱邮件

    一文详解如何使用Java来发送qq邮箱邮件

    这篇文章主要给大家介绍了关于如何使用Java来发送qq邮箱邮件的相关资料,文中降了准备工作(开启服务并生成授权码)、接口调用(引入依赖和编写接口代码)、发送HTML格式邮件等内容,需要的朋友可以参考下
    2024-12-12
  • 使用@Valid 校验嵌套对象

    使用@Valid 校验嵌套对象

    这篇文章主要介绍了使用@Valid 校验嵌套对象方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java图像之自定义角度旋转(实例)

    Java图像之自定义角度旋转(实例)

    这篇文章主要介绍了Java图像之自定义角度旋转(实例),需要的朋友可以参考下
    2017-09-09
  • java中如何使用泛型方法比较大小

    java中如何使用泛型方法比较大小

    这篇文章主要介绍了java中如何使用泛型方法比较大小,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Java 8 Lambda 表达式比较器使用示例代码

    Java 8 Lambda 表达式比较器使用示例代码

    这篇文章主要介绍了Java 8 Lambda 表达式比较器使用示例代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-08-08
  • Shiro安全框架的主要组件及认证过程简介

    Shiro安全框架的主要组件及认证过程简介

    这篇文章主要介绍了Shiro安全框架的主要组件及认证过程简介,Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证、授权、加密和会话管理,本文就来介绍一下此框架的核心组成,需要的朋友可以参考下
    2023-08-08
  • JVM的GC日志及运行参数解读

    JVM的GC日志及运行参数解读

    这篇文章主要为大家介绍了JVM的GC日志及运行参数解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09

最新评论