mybatis批量插入数据导致系统OOM,微服务内存爆了

这篇具有很好参考价值的文章主要介绍了mybatis批量插入数据导致系统OOM,微服务内存爆了。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

今天我们来说说工作中遇到的一个真实案例,由于使用mybatis的批量插入功能,导致系统内存溢出OOM(Out Of Memory), "java.lang.OutOfMemoryError: Java heap space"的报错,导致服务出现短暂的服务不可用,大概一两分钟不可用。这其实是个非常危险的故障,可能在高峰期导致整个系统瘫痪,服务不可用,产品不可用。

为了方便大家理解,我们就设想 这是一个学生表(student),我们要往这个表批量写入数据.


1. StudentEntity.java
Student对象实体

@Data
public class StudentEntity {
	private Long id ;
	private String name;
	private int age;
}


2. mybatis 生成的Mapper文件, 其中贴出了只是 批量插入 的方法
StudentMapper.java


public interface StudentMapper {

 /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table 'Student'
     *
     * @mbg.generated Mon Oct 24 16:35:18 CST 2022
     */
    int batchInsert(@Param("list") List<StudentEntity> list);	
}

    
3. mybatis 生成的Mapper xml文件, 其中贴出了只是 批量插入 的方法

StudentMapper.xml

	<insert id="batchInsert" keyColumn="id" keyProperty="id" parameterType="map" useGeneratedKeys="true">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon Oct 24 16:35:18 CST 2022.
    -->
    insert into student 
		(id, name, age)
    values
    <foreach collection="list" item="item" separator=",">
      (#{item.id,jdbcType=BIGINT}, #{item.name,jdbcType=VARCHAR}, #{item.age,jdbcType=INTEGER})
    </foreach>
  </insert>
  

4.  java service层批量插入数据

StudentServiceImpl.java

    @Service
    public class StudentServiceImpl implements StudentService {

        // service 层代码,参数是 StudentEntity list 数据,待插入到数据库中的数据
        @Override
        public void batchCreateStudent(List<StudentEntity> studentEntities) {
            // other code  ...
            
            studentMapper.batchInsert(studentEntities);
            // other code  ...
            log.info("  batchCreate success!! studentEntities size:{}", studentEntities.size());
        }
    }

到这里就出现问题了,就是上面这句  studentMapper.batchInsert(studentEntities); 导致服务出现 OOM的故障, java.lang.OutOfMemoryError: Java heap space. 

我相信很多人看到这个问题,也知道问题的原因是什么,其实就是mybatis 插件自动生成的批量插入语句是“有问题”的,通过该方法一次性插入 20万个对象,可能就会导致内存溢出,OOM了。 但是这个问题显然也不能怪mybatis,数据量太大,导致 Mapper类调用batchInsert()方法的时候,mybatis组装sql语句的过程中,堆内存溢出。

~~  mybatis 表示委屈,背不起这个锅 ~

mybatis批量插入数据导致系统OOM,微服务内存爆了

最终组装后的sql语句是这样的:

 insert into t_student

        (id ,name, age)
 values

        (1, '张无忌', 22),(2, '宋青书', 23) ... 
   ;

由于有20万个对象,在组装sql的过程中需要遍历这20万个对象,耗时特别长, 很容易导致api超时 timeOut,另外也会导致这个sql语句会特别的长。最终由于大数据量导致java堆内存空间溢出,超出了java heap space的空间大小。

· 其实这里的批量插入sql语句是有很多点可以拿出来说的,涉及到数据库批量插入空间配置,java堆空间的配置... 等等,今天我们就不深入说这个问题

 insert into t_student

        (id ,name, age)
 values

        (1, '张无忌', 22),(2, '宋青书', 23) ... 
   ;

快速解决办法:

既然是数据量太大,导致一次性批量插入内存溢出了,那很简单,我们就在应用里把数据拆分成小块数据,然后在分批的将小块数据插入到数据库中。就像 切分蛋糕,切分成一小块,一小块的。

mybatis批量插入数据导致系统OOM,微服务内存爆了

 
  5.  java service层批量插入数据,改进后,通过切分成小块数据,然后批量插入到数据库 

// service 层代码,参数是 StudentEntity list 数据,待插入到数据库中的数据

public void batchCreateStudent(List<StudentEntity> studentEntities) {
        // other code ...
        // studentMapper.batchInsert(studentEntities);

        // 先拆分成小块数据,再批量插入数据库中
        this.splitBatchInsert(studentEntities, list -> {
            studentMapper.batchInsert(list);
            return true;
        });
        // other code ...
        log.info("  batchCreate success!! studentEntities size:{}", studentEntities.size());
}
	
   
		
// 大数据量split成小数据量,进行小批量插入数据库中
private <T> void splitBatchInsert(List<T> list, Function<List, Boolean> insertFunc) {
        Integer batchSize = 1000;
        List<T> batch = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            batch.add(list.get(i));
            if (batch.size() == batchSize || i == list.size() - 1) {
                insertFunc.apply(batch);
                batch.clear();
            }
        }
    }

到这里,Java 堆内存溢出的这个问题应该就可能解决。 但是还有很多细节值得去思考,考虑。下面简单提下, 这篇文章就不展开说了:

假如改成上面拆分后插入还是内存溢出OOM的错误:

a. 一方面可以将上面的 阈值 1000 再改小,比如改成500 ;

b. 另外一方面 也可以将你的服务启动时,堆内存空间调大, 比如 改成 1500M, -server -Xmx1536m 。

ENV JAVA_OPTS="-server -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/jvm-logs/${APP_NAME}_jvm_dump.log "

最后将一些细节,可以在思考如何去不断的优化这个批量插入 的过程。又快又好又节省空间的方式

1) 上面写的阈值1000 ,需要根据自己的业务场景来设置,比如你插入对象简单,对象属性不读,也不复杂,单个对象的大小也不超过1kb,那你这里的阈值可以设置大些,比如2000, 甚至 可以是 10000;

2) mybatis生成的批量插入方法,可以继续优化,改用其他的批量插入方式,batchInsert();

3) Java内存空间的调整,到底要调整多大合适,如果真的需要调整,可能还需要谨慎操作;

4) 拆分成小块数据后批量插入,这个过程可以利用Java8 中的 parallelStream(), parallel()  也就是 Fork/Join框架来实现批量插入,分而治之的思想,是否也可以拿来解决该问题呢, 也值得我们探讨。文章来源地址https://www.toymoban.com/news/detail-429777.html

到了这里,关于mybatis批量插入数据导致系统OOM,微服务内存爆了的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot+MyBatis批量插入数据的三种方式

    最近导入表格数据时需要同时插入修改大量数据,研究了一下有三种实现方式 1、用for循环调用sql插入数据 这种方式插入大量数据时,效率非常底下,不推荐 2、利用mybatis的foreach来实现循环插入 这种方式插入大量数据时,好处是不用频繁访问数据库,一条sql搞定,效率比较

    2024年02月16日
    浏览(39)
  • MyBatis批量插入数据优化,增加一个参数,效率提升百倍

    项目中进行接口压测,发现批量插入的速度有点超出预期,感觉很奇怪,经过定位后发现mybatise-plus批量保存的处理十分缓慢,使用的是saveBatch方法,这点有点想不通。于是就进行了相关内容分析。 根据mybatise-plus中saveBatch的方法进行源码查看:   继续跟踪逻辑,从代码上看,

    2024年02月15日
    浏览(42)
  • 批量插入或更新数据(MyBatis-plus框架)

    目录 1.场景说明 2.DUPLICATE 和REPLACE比较 3.批量插入或者更新(两种方式) 方式一:mybatis-plus的saveOrUpdateBatch方法 问题:如果操作类集成了基础类,比如封装了BaseEntity去集成,那么这样使用会出问题 方式二:on duplicate key (推荐) 4.注意 5.常见问题  插入数据时,我们经常会遇到这

    2024年02月04日
    浏览(77)
  • 优化 - 重构一次Mysql导致服务器的OOM

    优化了一次前后端处理不当导致的CPU的一次爆机行为,当然,这和服务器的配置低也有着密不可分的关系,简单的逻辑学告诉我们,要找到真正的问题,进行解决,CPU爆机的关键点在于前后端两个方面,下面针对具体的问题,进行分析和解决。 定位问题 看监控的图表,CPU已

    2024年01月21日
    浏览(69)
  • Java8 实现批量插入和更新,SpringBoot实现批量插入和更新,Mybatis实现批量插入和更新

    基于mybatis实现的批量插入和更新 由于直接执行批量所有数据可能会出现长度超出报错问题,使用如下方式即可解决 原理还是分配执行,这里的100就是设定每次执行最大数 这里使用插入作为例子,也可以使用批量更新 更新的写法

    2024年02月12日
    浏览(52)
  • Mybatis批量插入

    使用Mybatis框架批量插入的3种方法:多次调用insert方法、foreach标签、batch模式 后端java代码:

    2024年02月12日
    浏览(58)
  • Mybatis批量插入、修改

            在 MyBatis 中, foreach 标签用于遍历集合类型的条件,并且可以将多个参数值拼接成为 SQL 语句的一个部分,通常被用于批量插入或更新等操作。  foreach属性及介绍          属性 介绍 collection 集合名称 item 字符别名 index 索引别名 open 循环前缀 close 循环后缀 separato

    2024年02月07日
    浏览(38)
  • Mybatis 中传入List实现 批量插入、批量更新、批量删除

    个人收藏使用 文章来自Mybatis 中传入List实现 批量插入、批量更新、批量删除 - chelsey3tsf - 博客园 (cnblogs.com) 1. 批量插入 : Mapper层: 对应的mapper.xml: 如果List数据量比较大,可以考虑将List分批次插入 2. 批量更新: 批量更新只提供更新单个字段的,因为更新多个字段无论哪种

    2024年02月11日
    浏览(57)
  • Mybatis批量插入方式有哪些

    MyBatis批量插入有多种写法,最后博主总结一些常见的批量插入写法供大家参考 使用XML配置文件进行批量插入:在XML映射文件中使用 insert 标签,并通过 foreach 标签迭代批量数据,然后在SQL语句中使用 VALUES 。 使用Java注解进行批量插入:在实体类上使用 @Insert 注解,并

    2024年02月11日
    浏览(42)
  • MyBatis的五种批量插入

    一.直接循环插入 最终耗时:14s多 二.关闭MySql自动提交,手动进行循环插入提交 平均:0.12s 第三种:用List集合的方式插入数据库(推荐) 第四种: MyBatis-Plus提供的SaveBatch方法 直接报错: 看报错信息: 长串:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw excep

    2024年03月15日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包