Linux CPU 100% 问题:一个 Shell 脚本解决
故障排查步骤
2.1 确定高负载进程的 PID
首先,登录服务器,使用 top
命令确认服务器的具体情况,然后根据情况进行分析判断。
通过观察 load average 和负载评估标准(8核),可以确认服务器存在高负载情况。观察各进程的资源使用情况,可以看到 ID 为 682 的进程 CPU 百分比相对较高。
2.2 确定具体异常业务
这里,我们可以使用 pwdx
命令根据 PID 找到业务进程路径,进而定位责任人和项目:
可以得出,该进程对应数据平台的 web 服务。
2.3 定位异常线程和具体代码行
传统解决方案通常涉及 4 个步骤:
1. top 按 P 排序:1040 // 首先按进程负载排序,找到 maxLoad(pid)
2. top -Hp 进程PID:1073 // 找到相关负载线程 PID
3. printf "0x%x" 线程PID:0x431 // 将线程 PID 转为十六进制,便于后续 jstack 日志搜索
4. jstack 进程PID | vim +/十六进制线程PID - // 如:jstack 1040|vim +/0x431 -
但对于线上问题定位,每一秒都很宝贵,上述 4 个步骤过于繁琐耗时。之前淘宝的 oldratlee 将上述过程封装成一个工具:show-busy-java-threads.sh
,可以方便地在线上定位此类问题:
可以得出,系统中一个时间工具类方法的执行 CPU 百分比很高。定位到具体方法后,检查代码逻辑是否存在性能问题。
根因分析
经过前面的分析排查,最终确定了一个时间工具类的问题,导致服务器负载和 CPU 使用率高。
- 异常方法逻辑:将时间戳转换为对应的具体日期时间格式。
- 上层调用:计算从当天零点到当前时间的所有秒数,转换为对应格式,并以集合形式返回结果。
- 逻辑层:对应数据平台实时报表的查询逻辑。实时报表会定时查询,单次查询中存在多次(n 次)方法调用。
可以得出,如果当前时间为上午 10 点,单次查询的计算次数为 10*60*60*n = 36,000*n 次,并且随着时间增加,每次查询的计算次数将随着接近午夜而线性增加。由于实时查询、实时告警等多个模块的大量查询请求需要多次调用该方法,导致大量占用和浪费 CPU 资源。
解决方案
定位到问题后,首先考虑的是减少计算次数,优化异常方法。经过排查,发现在逻辑层,方法返回的集合中的内容并没有被使用,而是简单地使用了集合的大小值。在确认逻辑后,通过新方法(当前秒数 – 零点秒数)简化了计算,替换了被调用的方法,解决了计算次数过多的问题。上线后,观察服务器负载和 CPU 使用率,相比异常时期下降了 30 倍,恢复到正常状态。至此,问题得到解决。
总结
在编码过程中,除了实现业务逻辑外,还需要注意代码的性能优化。能实现的业务需求,和能更高效、更优雅地实现,实际上反映了两种完全不同的工程师能力和境界,后者也是工程师的核心竞争力。
代码写完后,多做 review,思考是否可以用更好的方式实现。在线上问题中不要忽视任何小细节!细节是魔鬼。技术同仁需要有刨根问底的欲望和追求卓越的精神,只有这样才能不断成长和进步。
通过利用强大的工具如 show-busy-java-threads.sh
,并遵循系统的故障排查方法,你可以快速识别和解决 Linux 服务器上的 CPU 使用问题。始终追求优化的代码性能,以确保系统的稳定性和效率。