oracle数据库版本升级12后,线上服务突然每天白天不定时抽风。
1、调用端表现为接口调用超时;
2、Tomcat服务假死,查看Catalina.out日志缓慢输出,过几分钟应用OutOfMemory异常;
3、top查看linux负载飙高,tomcat进程CPU占用率超过100%,甚至达到1000%多;
刚开始调整JVM参数,增大持久代大小permsize=2g maxpermsize=4g,运行半天后又挂了;
增加dump输出,查看dump日志,由于太大也看不出啥异样,用MAT分析日志,工具一下就奔溃了;
查看异常日志前后,发现接口返回日志中的json串中,有key为$ref,value为 $xxxx.xxxx的元素,进一步发现json格式化时,会自动检测是否存在重复引用和循环引用的元素,如果存在,为了避免堆栈溢出,自动停止了递归,系统用的是阿里开源的fastjson,阿里还是很强大的,借此发现了程序里一些不规范的写法,进行了修正,但是问题依旧;
快奔溃了,数据库升级前都没做过应用升级,奇怪啊!
用户投诉越来越多,组长说重启主机试试吧。遇到棘手的问题,同事写了个应急脚本,如果cpu使用率超过90%就自动重启服务,虽然解决了用户投诉,但是还有些接口调用得手工处理,不是长久之计。
再想想是不是和数据库有关呢?查看oracle的awr日志,看能不能发现什么端倪。发现部分sql有性能问题,通过建立索引解决一些bug;发现redolog切换频繁,指标异常logfile switch(checkpoint incomplete),通过联系dba,调整logfile大小,程序好几天不挂了;过了几天问题突然又跑出来了,也许是这几天应用比较闲;
重新审视应用,尝试关闭mybatis日志输出:
调整jdbc参数:去掉参数removeAbandoned,removeAbandonedTimeout
问题依旧啊!!!
在自动重启脚本中,增加jstack输出,查看应用刚异常时堆栈问题;
通过几次异常堆栈的分析,总是有一个接口,但状态是RUNNING,不应该啊!!
针对该接口,查看接口调用日志表,查看异常前后,只有请求,没有返回的接口做了下统计,就是上面的这个接口,哪里不对?查看请求参数,接口代码,发现请求参数不按套路来啊, 接口服务端没有校验这种流氓请求,导致对数据库表的全量查询,这不挂才怪呢!!至此问题原因总算找到了,接下来就很简单,增加接口限制,这也提醒我们,编写接口时要规范,避免不确定性,偷懒的下场啊!!!
事情还没结束,反思一下问题核查过程,历时3个月左右,难道我们不能提前发现这样的问题吗??
回想10年行业经验,这种线上问题似乎总会遇到。与数据量有关,系统上线时数据量小,不会有性能问题;与接口编码规范有关,如果接口规范点编写,就不会有这样的问题了,还可以通过代码review机制提前发现;如果没有团队内代码review机制,可以通过在应用中增加检测层来提前发现应用异常;也可以在问题出现后,增加检测手段来尽快发现定位问题。避免人力物力的浪费。
如何检测?
针对dao层,检测sql运行影响的行数,设定阈值,增加预警。
这方面如果使用了Mybatis的话,打开mybatis日志,输出到ELK,或者其他alert工具中,即可有效监控。