Linux服务器CPU使用率飙到100%,最常见的直接原因是某个进程死循环或高并发请求堆积,优先用top确认进程PID,再按进程类型(业务代码、数据库、异常进程)分治解决,彻底定位比盲目重启更重要。
先花30秒定位:是谁吃光了你的CPU
服务器CPU跑满100%,系统通常还能SSH登录,别慌,第一步是找到元凶,登录服务器后,执行下面两条命令之一:
top回车,按P键按CPU使用率排序,看第一行进程PID和%CPU列- 用
ps -eo pid,ppid,%cpu,%mem,cmd --sort=-%cpu | head -n 10直接列出Top 10进程
如果看到某个进程长时间占用接近100%,先记录PID,接着用 lsof -p PID 看这个进程打开了哪些文件、连接了哪些网络端口;再用 ls -l /proc/PID/exe 确认这个进程的可执行文件路径和身份,这两步能帮你快速判断是自家业务进程、数据库进程,还是被入侵植入的挖矿程序。
行业共识认为,绝大多数CPU跑满案例都能在这一步完成初步定性:要么是业务代码出问题,要么是请求流量超预期,要么是系统被恶意利用,不要急着重启,先看清楚再动手。
按业务场景逐项排查:从代码到架构的完整链条
业务应用进程占用高:代码死循环、日志爆炸、线程阻塞
如果你的top里显示的是Java、Python、Node.js或者PHP进程,按以下顺序检查:
- 先看日志:
tail -n 200 /var/log/app.log或业务日志目录,死循环经常伴随着大量异常堆栈、重复错误、日志快速增长,如果日志文件几秒钟增加几十MB,多半是代码进入了异常循环或错误级别的日志刷屏。 - 检查线程栈:Java进程用
jstack PID | grep -A 30 "java.lang.Thread.State"看线程状态;对Python进程可用py-spy dump --pid PID;Node.js用node --prof-process生成CPU profile,多数情况下能看到线程卡在哪个方法或循环里。 - 审视最近的代码变更:回想或通过Git log查看上一个发布版本改了什么,高并发下最容易出问题的是缓存失效后穿透到数据库、正则表达式回溯、字符串拼接大对象等场景,举个例子,一个未加索引的慢SQL在业务高峰期被热点请求反复触发,数据库CPU会先暴涨,紧接着应用服务器CPU也会被连接等待拖垮。
数据库进程CPU飙高:慢SQL和索引失效是头号嫌疑
如果top
里看到mysqld、postgres或redis-server占用过高,别马上杀进程,先查数据库慢查询日志:
# MySQL mysql -uroot -p -e "SHOW FULL PROCESSLIST;" # 或开启慢查询日志后查看
常见原因和对应动作:
- 慢SQL没走索引:用
EXPLAIN SELECT ...查看执行计划,关注type列是否为ALL(全表扫描)或rows是否异常大,加索引或者改写SQL是首选方案。 - 锁竞争严重:看
SHOW ENGINE INNODB STATUS的LATEST DETECTED DEADLOCK和当前锁等待,业务高峰期大量并发更新同一行,会导致CPU等待锁自旋飙升。 - 缓存失效击穿:Redis不存在或者过期,请求全部落到数据库,先把缓存重建策略改成互斥锁或提前异步刷新,再考虑增加缓存时长。
高并发请求导致软中断和上下文切换飙升:网络层的信号
当CPU的us(用户态)不高,但si(软中断)或sy(系统态)很高,且top里找不到单个高占用进程时,问题往往出在网卡收包或内核处理上。
- 执行
cat /proc/softirqs,看NET_RX是否增长异常。 - 用
ethtool -S eth0 | grep -E "rx_packets|tx_packets"对比看包速率。 - 检查是否被CC攻击:
ss -state ESTABLISHED | wc -l看连接数,再用ss -state ESTABLISHED -field remote | sort | uniq -c | sort -nr | head找到异常来源IP。
如果确认是流量攻击,第一时间在防火墙或云安全组里封掉异常IP,再调整nginx和内核参数,比如增加net.core.netdev_max_backlog、调低net.ipv4.tcp_syn_retries,安全组拦截和DDoS高防防护是云服务器厂商的标准能力,具体配置方法可以咨询平台客服。
处理CPU100%的实操步骤:从止损到治本
第一步:快速止损,但别盲目kill
如果是业务进程,先把进程挂起或重启,让服务恢复响应,但保留现场:
# 保留CPU核心转储(部分场景对Java/DTCore有用) gcore -o /tmp/pidcore PID # 杀掉或重启异常进程 kill -9 PID # 对于守护进程,使用systemctl restart app.service
重启后业务恢复,但问题没解决,还会再犯,现场数据(core文件和日志)是后续定位的依据,没有它就只能猜。
第二步:看系统负载和CPU时间分布
执行vmstat 1 5,观察username(us)、system(sy)、idle(id)三列,如果us高,是用户态代码问题;如果sy高,可能是虚拟机频繁调度或内核锁问题;如果id低且wa高,则CPU在等待磁盘I/O。
再执行uptime看load average,注意CPU核心数和负载的对应关系:8核机器负载跑到8,满载;16核跑到12,还有余量,但load高和CPU100%不是一回事,负载还包括不可中断的D状态进程,比如卡在磁盘I/O上的进程。
第三步:针对不同根因的具体处置
| 根因类型 | 典型特征 | 直接处置 | 长期方案 |
|---|---|---|---|
| 业务代码死循环 | 单进程CPU恒定90%+,无异常日志 | 重启进程,收集线程栈 | 引入代码评审、压测,使用APM工具监控 |
| 慢SQL打爆数据库 | 慢查询日志增加,数据库进程CPU高 | 杀掉慢查询会话 | 优化索引、引入读写分离、分库分表 |
| 流量攻击 | 网络连接数激增,软中断狂飙 | 封IP、启用云安全组 | 接入高防,配置限流、WAF |
| 内存不足触发Swap | 服务器卡顿但CPU不高,swap使用率高 | 扩充内存或重启业务 | 增加内存监控,配置OOM保护 |
| 被挖矿木马入侵 | 陌生进程占满CPU,有异常外联IP | 杀进程、删除文件 | 修复漏洞、改密码、更新安全组 |
第四步:检查是否被挖矿程序渗透
近年来的安全报告反复提到,Linux服务器CPU100%的一个常见原因是中了挖矿木马,挖矿程序的特征非常明显:
- 进程名伪装成
kworker、systemd、crond等常见名字,但实际路径在/tmp或/var/tmp下 - 网络连接大量访问矿池端口,比如4444、8333、9999等
- CPU使用率在满负荷和低负荷之间周期性波动,佣金账户切换频繁
处理步骤:先用nettop -n或ss -tnp找到外联地址,按PID定位程序路径,删除可执行文件;然后检查crontab、systemd服务、rc.local和shell历史,清除全部持久化后门,如果是在公有云上,建议直接重装系统,并修改SSH密钥、控制台密码,这是最彻底的做法。
如何提前预防CPU100%再次发生
站在运维角度,等CPU满了再处理是被动的,好的做法是提前建立防线:
- 配置监控告警:为CPU使用率设置两个阈值,比如60%预警、85%触发告警,业内专家指出,监控数据的采样周期不要超过1分钟,否则峰值容易漏掉,用云平台的监控报警或者开源的Prometheus+Grafana都行。
- 限制单个进程资源:用
systemd的CPUQuota字段限制服务CPU上限,或者用cgroup给数据库、Web服务分别划分核数,例如在某个服务单元文件里加CPUQuota=200%表示最多用2个核心。 - 定期压测和容量评估:每半年做一次全链路压测,模拟业务峰值流量,不少线上事故都出现在搞完大促、做活动或者上线新功能后,压测能提前暴露代码瓶颈。
- 把关键日志和线程快照接入自动转储:Java应用可以配置
-XX:+HeapDumpOnOutOfMemoryError,Python进程写一个定时抓取栈的脚本,这样CPU一旦异常,你已经留有现场证据。
Q&A:关于Linux服务器CPU100%的常见疑问
为什么CPU100%但网站还能打开?
多数情况下是某一颗核或单个进程吃满,系统其它核心尚有富余。top命令第一行的%Cpu(s)展示的是所有核的平均值,必须按1查看每个核的使用率,Web服务器通常有进程/线程池隔离机制,少量进程出问题不会让整个服务瘫痪,如果一个进程占满了所有核,网站还能打开,多半是网络栈和静态资源响应未受影响,但动态请求基本已经全部卡住了。
云服务器CPU100%和物理机处理方式有什么不同?
云服务器的CPU跑满会受到账号限流,如果购买的是共享型实例,CPU长时间占满可能导致丢包或降频,甚至被平台强制停机,处理上比物理机多一步:先确认实例规格和CPU积分(像t系列实例会有CPU信用额度),如果积分耗尽,就要考虑升级到计算型实例或者独享型实例,云平台控制台的安全组、流量攻击告警和快照回滚功能,能节省大量排查时间,处理流程和物理机一样,但止损动作更快,回滚也更容易。
用kill杀掉高CPU进程会不会把系统弄崩?
只有在明确知道进程身份和影响范围时才能杀,如果是你自己的业务进程,kill后服务会中断,但系统本身不会崩,你真正要担心的是数据库和关键中间件进程,直接kill -9可能造成数据损坏,安全做法是尽量用服务自带的重启命令,比如systemctl restart mysql,处理前先发信号让进程优雅退出,如果进程是你的业务代码,杀掉后最好保留日志和栈现场,否则只能等下次复现。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/715073.html





