一次线程java.lang.OutOfMemoryError(OOM)协查后的思考

上周一同事找我协查一个线上OOM的问题,协查的过程这里就不讲了,跟我之前协过的一个OOM过程其实差不多,之前的协查参看这篇:通过mybatis源码,分析一次由mybatis使用不当导致的OutOfMemoryError的协查

今天主要讲一下结果和对结果做一下总结:

这次的OOM的发生主要由于,一个不常用的查询功能,做的全表查询,因为数据量太大,导致的堆内存溢出

先来看一sql(列了一个相近的sql,不是线上的sql)mybatis的xml里的一个片段:

这个片段是页面的一个查询功能最终要执行的sql,where条件里的三个值来自己页面用户的输入

select * from t_user usr
<where>
<if test=" email != null" >
and usr.email = #{email ,jdbcType= varchar}
</if>
<if test=" certNo!= null" >
and usr.cert_no = #{ certNo,jdbcType= varchar}
</if>
</where> 

这里所有的where条件里都了为空的判断,在生产中,恰巧用户都没填写,最终执行的sql变成了select * from t_user usr 导致全表数据被查询出来,而且这张表里有1000W以上数据,用户多点了两点,系统就挂了

说一下这里的几个问题

1.数据没有做分页

当我们的数据过多时,查询一定要做分页,而且是数据分页,最好不要在内存和页面端做分页,其它mysql数据单表到了千万行后单表怎么优化性能都不会特别的好,这里大家可以考虑做一些分表分库的优化

2.页面没有做重复提交处理

当我们查一些大数据或者查询的sql已经很慢的时间,至少要在前端控制,在后端没有返回结果时,不要让用户再次点击,以免造成系统挂掉

3.前端没有做用户输入校验

前端应该要做输入校验,不允许用户一个条件都不输入,其实有些同学已经想到,会有些办法绕过页面检查,这里就需求第4步

4.后端没有做输入校验

因为总会有一些人可以绕过页面(前端的检查只能防君子),后端的一些校验就很有必要。

5.sql层没做查询保护

为了保证程序更加的健壮,其实在sql层,我们发现业务条件都是动态时,就可以提现做好保护工作,上面的sql可以像下面这样书写,防止(查询全部被脱裤或者全表扫描导致服务夯死):

select * from t_user usr
<where>
<if test=" email != null" >
and usr.email = #{email ,jdbcType= varchar}
</if>
<if test=" certNo!= null" >
and usr.cert_no = #{ certNo,jdbcType= varchar}
</if>
<if test=" email = null" >
<if test=" certNo= null" >
and  1=2
</if>
</if>
</where>

6.开发这个业务没有好好梳理业务,理解需求

协查时,查到这个表有1000w数据,开发当时都惊讶,我了解到,当初开发的时间,开发觉得这个功能不常用,并且也不太清楚会有多少数据进表。这个就是没有理解好需求提前评估好数据,如果好好跟需求沟通,确认每天的数据,提前做好设计这里应该就不会挖个坑

总结一下:

开发时一定要多了解业务多跟需求方沟通,了解了功能的本质再做需求开发。同事开发过程中,也要多考虑接口异常情况,用户永远不会按你想的操作,再一个就是大家多运用一些工具解决问题!

猜你喜欢

转载自blog.csdn.net/kevin_mails/article/details/99682178