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效率更高且不易发生问题。

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

相关文章

  • 详解SPI在Dubbo中的应用

    详解SPI在Dubbo中的应用

    通过本文的学习,可以了解 Dubbo SPI 的特性及实现原理,希望对大家的开发设计有一定的启发性
    2021-06-06
  • java图片压缩工具类

    java图片压缩工具类

    这篇文章主要为大家详细介绍了java图片压缩工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • java实现字符串和日期类型相互转换的方法

    java实现字符串和日期类型相互转换的方法

    这篇文章主要介绍了java实现字符串和日期类型相互转换的方法,涉及java针对日期与字符串的转换与运算相关操作技巧,需要的朋友可以参考下
    2017-02-02
  • Java输出通过InetAddress获得的IP地址数组详细解析

    Java输出通过InetAddress获得的IP地址数组详细解析

    由于byte被认为是unsigned byte,所以最高位的1将会被解释为符号位,另外Java中存储是按照补码存储,所以1000 0111会被认为是补码形式,转换成原码便是1111 0001,转换成十进制数便是-121
    2013-09-09
  • 详细解读Java Spring AOP

    详细解读Java Spring AOP

    这篇文章主要介绍了Java Spring框架中AOP的工作原理,AOP面向切面编程其实也可以被看作是一个设计模式去规范项目的结构。需要的朋友可以收藏下,方便下次浏览观看
    2021-12-12
  • 浅谈Java中的n种随机数产生办法

    浅谈Java中的n种随机数产生办法

    众所周知,随机数是任何一种编程语言最基本的特征之一。而生成随机数的基本方式也是相同的:产生一个0到1之间的随机数。看似简单,但有时我们也会忽略了一些有趣的功能。
    2015-09-09
  • spring boot发简单文本邮件案例

    spring boot发简单文本邮件案例

    这篇文章主要介绍了spring boot发简单文本邮件案例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • java连接mysql数据库学习示例

    java连接mysql数据库学习示例

    这篇文章主要介绍了java连接mysql数据库学习示例,需要的朋友可以参考下
    2014-03-03
  • 解决SpringMvc中普通类注入Service为null的问题

    解决SpringMvc中普通类注入Service为null的问题

    这篇文章主要介绍了解决SpringMvc中普通类注入Service为null的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 使用eclipse创建java项目的方法

    使用eclipse创建java项目的方法

    这篇文章主要为大家详细介绍了使用eclipse创建java项目的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04

最新评论