写点什么

记一次线上事故

用户头像
编号94530
关注
发布于: 2020 年 05 月 14 日
记一次线上事故

1. 事故发生

某天晚上,收到系统CPU占用过高报警,立刻登录服务器查看cpu信息(top,命令即可),此时CPU占用率高达750%+, 查看GC日志,频繁的发生Full GC, 并且一次Full GC市场可达到6s,立刻使用jmap命令dump文件(命令:jmap -dump:file=heap.bin <pid>)。立刻重启服务,恢复一部分机器,保证服务可用。重启后,系统恢复正常。



2. 堆栈分析

把dump的文件从服务器弄下来,然后使用MAT工具分析(dump的文件有点大,注意调整MAT内存大小,对MAT不熟悉的老铁,看看这个网址 MAT 官方文档)。



2.1 以下非线上事故文件分析(是分析整个流程)

使用MAT文件,看大这个首页,通过这个首页可以看到dump的时候,系统是什么情况,什么线程持有多大对象,都可以看出来。

这个当然不是主要对,点击进入Histogran,可以看到内存中,什么对象最多。如图:

很惨,只能看到是byte[]数组最多,没有特别多的有用价值,接着打开Dominator Tree, 这个里面有大致请求

很惨,只是发现了是ArrayList对象过大,里面存了很多东西,咋办呢?



2.1 堆栈分析

好了,我们不卖关子了, 上面是分析的一部分,有时候可以直接看出什么对象过多,便于确定问题,接下来是我认为重要的部分。线程堆栈信息。

点击首页这个线程信息,找到Java Basics,可以看到有两个关于线程堆栈信息的, 点进去我们,我们就可以看到当前线程的信息。话不多说,点进去。

好家伙,点进来发现了线程的堆栈信息,这么多链接,一看就是数据库操作的问题了,找到具体的代码调用栈(图中马赛克是具体代码信息)。找到具体的代码信息,就可以去研究代码了。



至此,我们找到了出现问题的地方,就可以具体到代码中出现问题的原因了。



3. 代码分析



通过分析代码, 我们发现,是代码在拼接sql的过程中,由于条件缺失,导致sql的查询条件被无线放大,所以,查询出来数据表中的超级多数据( ̄□ ̄||)。给大家举个例子,如下。

public void example() {
// tb_name 表里 create_time > '2020-01-01' 有几百万条记录
String sql = "select * from tb_name where 1 =1 and create_time > '2020-01-01' ";
int a = 0;
int b = 10;
if (a < -1) {
sql += " and user_id = " + a;
}
if (b > 5) {
sql += "and age > " + b;
}
q.query(sql);
}

最后修改代码,重新上线, 问题得到彻底解决。



4. 故障收获

大家在写代码的时候,特别注意条件的判断,对传入的数据进行一定的校验,通过自己判断,及早的返回结果,否则小心就会进行如上事故了。当然,有的老铁自己公司,对sql进行aop拦截,涉及到查询的,一般会有一个最大返回量(mybatis plus 3.+版本好像有实现,太久没注意哈),这种也是一种可取的方案。



5. MAT工具



MAT工具是要梯子下载的哈



发布于: 2020 年 05 月 14 日阅读数: 163
用户头像

编号94530

关注

你的每一个点赞我都当成了喜欢 2020.04.29 加入

公众号: 星球x 欢迎大家关注,谢谢!

评论

发布
暂无评论
记一次线上事故