云计算百科
云计算领域专业知识百科平台

服务器CPU 100%的问题的排查解决之路

网上的诸多答案我不太能接受,不够落地。

至少也得弄清楚面试官所说的“服务器”,到底是数据库服务器还是应用服务器吧,要知道两者的排查思路完全不同,且前者出现CPU 100%的几率更大。

下面我们分别从数据库服务器和应用服务器的角度,分别聊一下排查思路和解决方案。

数据库服务器

造成数据库服务器CPU 100%的原因如下:

(1)数据库的QPS或TPS整体提升导致;

(2)新增的一个或者几个慢查询SQL导致;

(3)原因(1)(2)共同导致;

目前主流的系统部署方式都是上云的,阿里云、腾讯云、AWS等云厂商都自带数据库监控,我们直接用它来排查即可。

排查思路其实很简单,对于原因(1),我们直接在数据库监控上看当前的QPS和TPS,对比于正常时段是否飙高就能看出来。

如下图所示:

具体解决方案的话,可以在业务系统侧进行限流、降级,将系统QPS或TPS降低到数据库所能承载的量级。

对于原因(2),我们看慢查询列表中是否有新增慢查SQL,且该SQL执行时间超长,或执行次数很多。

如下图所示:

对于这种新增慢查SQL,很有可能是最近的系统上线发布导致的,我们可以马上对系统进行回滚操作,这是最常见、且见效最快的解决方案。

除了回滚方案,也可以对该SQL所对应的系统功能进行降级操作,前提是这个系统功能为非核心功能。

原因(3)不必多说,上述两种排查思路和解决方案一起用即可。

应用服务器

应用服务器的CPU 100%问题排查起来要复杂一些,下面我来一一拆解下步骤。

问题排查步骤

(1)先通top命令定位出消耗CPU最高的进程

css

体验AI代码助手

代码解读

复制代码

top

图片来自网络,侵删

(2)继续通过top命令定位出消耗CPU最高的线程

css

体验AI代码助手

代码解读

复制代码

top -H -p <PID> # 显示目标进程下所有线程

图片来自网络,侵删

(3)将线程的TID转换为16进制,以备后用

perl

体验AI代码助手

代码解读

复制代码

printf "%x\\n" <TID>

图片来自网络,侵删

(4)通过jstack + gerp组合命令,查找目标线程的堆栈信息

xml

体验AI代码助手

代码解读

复制代码

jstack <PID> | grep <TID>

图片来自网络,侵删

也可以将其输出为文件

xml

体验AI代码助手

代码解读

复制代码

jstack <PID> | grep <TID> -A50 >> thread_log.txt

接下来根据查找到的线程堆栈信息,我们就可以进行一轮问题定位分析了。

问题定位思路

接下来说几个我多年积累的,可以帮助大家快速定位CPU 100%问题的思路。

(1)当线程处于RUNNABLE状态,可能是代码中出现了计算密集型的任务,或是循环、递归次数过多,甚至是出现了死循环的情况。

(2)当线程名为GC Task Thread,可能出现了频繁GC的情况,需要结合GC日志进行分析。

(3)当大量线程处于BLOCKED状态,可能是代码中出现了锁竞争的情况。

(4)如果CPU利用率很高的线程一直是同一个,直接对其进行分析即可,反之,CPU利用率偏高的线程不断变化,则很有可能是线程创建过多导致。

(5)我们尝试用“deadlock”作为关键字进行搜索,若搜索到相关信息,排查一下代码中的死锁问题。

问题解决思路

其实定位到问题之后,接下来的解决方案就比较容易做了。

(1)对于计算密集型的任务,我们可以先从代码的角度入手,看看是否有优化空间,比如:减少数据的计算量,或者换一种更省CPU的算法等。

如果在代码层面已经优化到极致了,那可以直接选择从硬件层面进行扩容,均摊计算压力。

(2)对于代码中的死循环问题,我们可以通过添加可中断标志位、限制最大执行次数和CPU资源控制等方式,降低CPU使用率。

可中断标志位和CPU资源控制,代码如下:

java

体验AI代码助手

代码解读

复制代码

// 使用 volatile 保证可见性 private volatile boolean running = true; public void startLoop() { new Thread(() -> { while (running) { // 业务逻辑 processData(); try { // 避免忙等待,适当休眠,降低CPU使用率 Thread.sleep(100); } catch (InterruptedException e) { // 恢复中断状态 Thread.currentThread().interrupt(); break; } } }).start(); } // 安全终止循环 public void stopLoop() { running = false; }

限制最大执行次数,代码如下:

java

体验AI代码助手

代码解读

复制代码

final int MAX_ITERATIONS = 1000; int count = 0; while (count++ < MAX_ITERATIONS && !isTaskDone()) { executeStep(); } if (count >= MAX_ITERATIONS) { logger.warn("循环达到最大次数,强制退出"); }

(3)对于频繁GC的问题,需要结合GC相关工具一起进行排查,一般是由于堆内存设置不当、内存泄露、大对象分配等原因。

(4)对于代码中锁竞争的问题,可以通过缩小锁粒度、使用无锁化数据结构、使用Lock中的tryLock()、 tryLock(long time, TimeUnit unit)等非持续阻塞的方式进行解决。

tryLock(long time, TimeUnit unit)超时中断的方式,代码如下:

java

体验AI代码助手

代码解读

复制代码

import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; public class LockExample { private final ReentrantLock lock = new ReentrantLock(); public void execute() { // 尝试获取锁,最多等待100毫秒 if (lock.tryLock(100, TimeUnit.MILLISECONDS)) { try { // 在此处执行获取锁后的业务代码逻辑 System.out.println("Lock acquired, work performed here."); } catch (Exception e) { // 处理异常 e.printStackTrace(); } finally { // 确保释放锁 lock.unlock(); } } else { // 在此处执行没有获取锁的业务代码逻辑 System.out.println("Could not acquire lock, work performed here."); } } public static void main(String[] args) { LockExample example = new LockExample(); example.execute(); } }

(5)对于代码中的死锁问题,可以通过顺序加锁的方式解决。

除此之外,也可以使用无锁化数据结构、使用Lock中的tryLock()、tryLock(long time, TimeUnit unit)等非持续阻塞的方式进行优化。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 服务器CPU 100%的问题的排查解决之路
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!