记一次线上事故

1. 事故发生

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

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工具是要梯子下载的哈,没有的老铁可以多找找

猜你喜欢

转载自www.cnblogs.com/lifacheng/p/12892121.html