记一次IN语句造成的Hibernate QueryPlanCache内存泄漏排查和解决办法

共 1563字,需浏览 4分钟

 ·

2023-07-11 12:27

如果您使用了Hibernate或Spring data jpa,且SQL中使用了IN语句,且每过一段时间JVM就抛OOM异常,那么这篇文章可能对您有用。

1.问题现象

每过三四天时间,JVM就抛出OOM异常,后台进程挂掉,前端无法访问。

2.问题排查

首先,查找一下后台进程的进程号

    ps -ef | grep "程序名"
  

然后,使用jmap命令生成内存镜像文件

     jmap -dump:live,format=b,file=heap.hprof 4447
  

最后,使用 MAT 对内存镜像文件进行分析,分析结果如下图所示:

e13d54ab4a9c244268b5d92c6b38c640.webp

从Problem Suspect 1 中可以看出一个SessionFactoryImpl类型的实例占用了93.14%的内存。点击Details,可以看到SessionFactoryImpl类中有一个queryPlanCache对象,占用了大量的内存。

fb925994b04457e2afce9c7d1e0f06c3.webp

百度了一下queryPlanCache内存泄漏,找了一篇和我问题相似的文章 《Hibernate sessionFactoryImpl QueryPlanCache 内存过大导致内存泄漏》 。文章提到了QueryPlanCache会缓存sql,以便于后边的相同的sql重复编译,如果in后的参数不同,hibernate会把其当成不同的sql进行缓存,从而缓存大量的sql导致heap内存溢出。

2.解决方案

第一种方法是添加配置,现在缓冲的sql数量最大为64(但是我用的spring 1.57.release版本添加该配置没有用)

    spring:
    
jpa:
  properties:
    hibernate:
      query:
        plan_cache_max_size: 64
        plan_parameter_metadata_max_size: 32

所以,我对SQL进行了改写,之前我的SQL逻辑是这样的

    # 1.从TOPIC_TOPIC_TYPE表中将帖子类型为1的帖子ID找出来
    
SELECT DISTINCT t.topic_id FROM TOPIC_TOPIC_TYPE t WHERE t.type_id=1
# 2.从TOPIC表中按帖子ID进行查询
SELECT * FROM TOPIC t WHERE t.id IN ?;

使用子查询进行优化,优化后没有再出现内存泄漏问题。

    SELECT * FROM TOPIC WHERE id IN (
    
SELECT DISTINCT topic_id FROM TOPIC_TOPIC_TYPE WHERE type_id=1
);

但是后面使用EXPALIN发现,IN语句走的是全表扫描,性能比较低,所以又用JOIN进行了优化。

    SELECT * FROM TOPIC t1 JOIN TOPIC_TOPIC_TYPE t2 ON t1.id=t2.topic_id AND t2.type_id=1;
  
浏览 236
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报