【开发篇】七、mybatis的foreach遍历,SQL拼接导致内存溢出

发布于:2024-03-31 ⋅ 阅读:(67) ⋅ 点赞:(0)

在这里插入图片描述

1、背景

文章微服务升级,新增了一个传入文章id的List,判断有多少id是存在的接口,第二天高峰期内存溢出。

2、快照文件分析

在这里插入图片描述

打开直方图,发现线程对象占用排第一。打开支配树,按深堆排序,选占用最大的线程对象,找到处理器方法HandlerMethod,List objects --> with outgoing references查看其关联的对象

在这里插入图片描述

在description中方找到当前线程在执行哪个方法

在这里插入图片描述

再回到支配树,发现有个字符串对象,深堆非常大,里面是一句SQL,而字符串底层是用字符数组实现的,其下方的char有157w大小,且char的浅堆也很大,点击发现里面是frch_item,而它的产生是因为SQL在拼接过程中用了foreach去遍历了一个大集合:

在这里插入图片描述
在这里插入图片描述

3、本地环境复现

找到快照文件中的相关代码:

@RestController
@RequestMapping("/sqljoint")
public class DemoSqlJointController {

    /**
     * 服务对象
     */
    @Resource
    private TbArticleService articleService;

    /**
     * 判断批量id存在多少个
     * size:传入生成的id数量
     */
    @GetMapping
    public ResponseEntity countIfAbsent(int size) {
        //随机生成批量id,模拟传入一个id的List
        List<Integer> ids = new Random().ints(0, 1000000).
                limit(size).boxed().collect(Collectors.toList());

        return ResponseEntity.ok(this.articleService.countIfAbsent(ids));
    }

}

Mapper层用foreach做了一个遍历

<!--判断当前id在不在数据库中,在就记为1,不在就记为0-->
    <select id="countIfAbsent" resultType="java.lang.Long">
        select
        IFNULL(sum(1),0)
        from article where
        <if test="ids != null and ids.size() > 0">
            id in
            <foreach collection="ids" item="item"  open="(" close=")" separator=",">
                #{item}
            </foreach>
        </if>
    </select>

在本地启动Jmeter,调整size的值

在这里插入图片描述

Visual发现size大时,JVM堆内存溢出

4、结论

Mybatis在使用foreach进行sql拼接时,会在内存中创建对象,如果foreach处理的数组或者集合元素个数过多,会占用大量的内存空间。

5、解决方式

  • 限制id个数的最大值
  • 将id缓存到redis,先用内存查
本文含有隐藏内容,请 开通VIP 后查看