Redian新闻
>
同事写了一条 SQL,把 MyBatis 都干翻了。。

同事写了一条 SQL,把 MyBatis 都干翻了。。

公众号新闻
点击关注公众号,Java干货及时送达

作者:Lxlxxx

链接:https://juejin.cn/post/7221461552343072828

前言

继上次线上CPU出现了报警,这次服务又开始整活了,风平浪静了没几天,看生产日志服务的运行的时候,频繁的出现OutOfMemoryError,就是我们俗称的OOM,这可还行!

频繁的OOM直接会造成服务处于一个不可用的情况,通过Skywalking查看链路调用,基本全报红了,基本处于一个瘫痪状态,因为生产该服务是分布式部署,运维当即立断对该服务进行重启,因为是B端的产品,先让公司业务能用起来了,保证服务的正常使用,然后紧急查看问题,当然这个问题就来到了我这里,既然分配给我了,咱高低给它查出来,并且修复了。

OutOfMemoryError出现的原因

先来了解下OutOfMemoryError出现的原因,无非就是两类堆内存空间不足、元空间不足

  1. 堆内存空间不足:意味着程序存在一直有引用的对象(强引用),主要对象在引用的状态就无法被GC回收,撑爆了-Xmx堆拓展的最大值,内存不足自然就会触发堆内存溢出。
  2. 元空间:Java 8引入了元空间概念,代替了之前堆的永久代,由于元空间属于堆外内存,不需要有对象引用,通过指针的方式表示类和元数据,之所以引用元空间就是一种JDK的升级优化,避免了永久代的内存溢出。

常见堆内存溢出的几种情况

  1. 查询数据库返回的数据量过大,加载到内存中导致内存溢出;
  2. 代码中出现死循环情况,导致大对象一直被引用不能被GC回收;
  3. 资源链接池、io流在使用完没有进行手动释放;
  4. 静态集合类里面存在引用对象,始终存在引用关系,没有进行清除;

以上属于常见的几种堆内存溢出的场景,当然有时候我们的遇到的问题都是稀奇古怪的问题,常见的问题总是很少能遇到…

推荐一个好用的ChatGPT网站:

ChatGPT中文网站:https://svip.cxyquan.com

现象分析

根据生产环境的报错日志来看,这边属于Mybatis报出的一个内存溢出情况,通过去看Mybatis源码发现,底层也是通过一些集合类来存放拼接的sql,那么当然也有可能出现堆内存溢出,而且在sql体积比较大的情况下,接收sql的集合就会变的非常大,如果回收不了那么就会导致内存溢出。

由于我们docker容器里面没有一些jstack、jmap的工具,并且dump文件也没有进行保存…导致我无法通过看线程高占用内存的对象,来分析具体是什么操作发生的内存溢出,这就难了… 

于是只能去网上搜搜看了,没想到真的给到我一些启发,并且有点思路大概知道是哪里的问题。

文章来源于zzzzbw作者写一篇关于 惨遭DruidDataSource和Mybatis暗算,导致OOM ,很感谢🙏这位作者给我带来的启发。

文章作者也遇到了Mybatis带来的OOM,主要是因为Mybatis拼接SQL的时候生成的占位符和参数对象,存放在Map里,当SQL的参数多导致SQL太长的时候,Map持有这些SQL时间较长,并且多线程同时操作,这时候内存占用就很高,从而发生OOM

Mybatis源码分析

通过对DynamicContext类源码查看,DynamicContext又一个ContextMap 类型的参数bindings,继承了HashMap相当于一个Map集合,接着看这个类中的getBindings方法,看到了ForEachSqlNode这类调用了getBindings方法,简单的说就是ForEachSqlNode通过getBindings方法,将SQL参数和参数的占位符统一put到ContextMap这个集合里面,主要是这里面的参数和占位符无法被GC回收,并发查询量多的情况下就会导致OOM。

情景复现

随后我做了线上场景的复现,通过将SQL语句的拼接,将IN里面的参数变大,然后创建50个线程进行执行,将JVM堆内存设为-Xmx256m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

这里看控制台打印的日志,服务在频繁的进行Full GC,导致OOM。

总结

既然发现了问题出现的原因,接下来就是对代码SQL进行优化,尽量避免在sql拼接的时候体积过大,这里告诫我们代码不能乱写,SQL语句也不能随意写啊,有时候把问题想的过于简单确实会带来不可预知的风险。

PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。

(完)




加我"微信获取一份 最新Java面试题资料

请备注:666不然不通过~


最近好文


1、不好意思,Mybatis Plus 该换了!

2、MySQL中update“经典”的坑,这样写语句,直接劝退!

3、SpringBoot:一个注解就能帮你下载任意对象

4、Controller 层代码就该这么写,简洁又优雅!

5、一台服务器最大能支持多少条 TCP 连接



最近面试BAT,整理一份面试资料Java面试BAT通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:关注公众号并回复 java 领取,更多内容陆续奉上。
明天见(。・ω・。)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
用一个月重构了同事写的烂代码,我总结了8条重写烂代码的经验!“MySQL 之父”的 MariaDB 要完蛋了?叫停两款核心产品并裁员 28%,分析师:该行为无异于自毁长城MySQL到底是 join 性能好,还是in一下更快呢?基于MySQL多通道主主复制的机房容灾方案MYSQL事务的底层原理DoltgreSQL发布,基于Git的PostgreSQL重磅 |《开源数据库生态发展研究报告》发布 GreatSQL为MySQL5.7最佳替代方案!怪异杂谈-第一章 未燃尽的蜡烛MySQL数据导入方案推荐SQL骚操作,一条SQL 统计近 7天、30天、全部的订单量如何设计一款基于 MySQL 实现的 Message Queue为何在中国MySQL远比PostgreSQL流行Nginx 代理 MySQL 连接,并限制可访问 IP阿里终面:10亿数据如何快速插入MySQL?怪异杂谈-第三章 云海的故事基于 MySQL 多通道主主复制的机房容灾方案“MySQL 之父”的 MariaDB 要完蛋了?叫停两款核心产品并裁员 28%一网打尽总结 Mysql 的所有 BufferMySQL到TiDB:Hive Metastore横向扩展之路4 种 MySQL 同步 ES 方案,yyds!MySQL备份恢复最佳实践:终极指南Python如何使用MySQL 8.2读写分离?Next.js支持在前端代码中写SQL,开倒车还是遥遥领先?MySQL主从同步延迟原因与解决方案弃用 MySQL 后存储成本降低 85%,携程业务系统数据库升级技术实践MySQL中update“经典”的坑,这样写语句,直接劝退!怪异杂谈- 第二章 拐角处那些相似的人一百二十九 宿京口MySQL如何性能调优?上篇MySQL 8.2 正式可用,支持读写分离怪异杂谈-第四章 前世的想念与渊源Mysql集群之PXC-Docker安装MySQL 分库分表实践MySQL binlog 三个典型的业务应用场景聊聊即将到来的MySQL5.7停服事件
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。