Linux服务器突然卡了?5个排查步骤一次搞定
场景引入
你一定遇到过这样的场景:周一早上刚到工位,手机就炸了——客户反馈网站打不开,老板在群里@你,同事焦急地催你。你赶紧SSH登录服务器,发现敲命令后要等好几秒才有反应,ls 都卡顿,top 命令更是转了十几秒才出来。更糟的是,你连 reboot 都不敢执行,怕万一重启后起不来了。
这种“服务器突然卡了”的紧急情况,是每个运维人都会遇到的噩梦。别慌,今天我就带你走一遍完整排查流程,5个步骤让你从“手抖”变成“稳如老狗”。
问题复现
我们先模拟一个典型的“卡顿”场景。假设你有一台跑着Nginx + PHP-FPM + MySQL的Web服务器,突然某个时段CPU飙升到100%,所有请求都超时。
登录后,你执行 top 命令,看到类似这样的输出:
top - 10:15:30 up 3 days, 2:10, 1 user, load average: 15.20, 12.80, 10.50Tasks: 250 total, 3 running, 247 sleeping, 0 stopped, 0 zombie%Cpu(s): 95.0 us, 3.0 sy, 0.0 ni, 0.0 id, 2.0 wa, 0.0 hi, 0.0 si, 0.0 stKiB Mem : 8000000 total, 200000 free, 6000000 used, 1800000 buff/cacheKiB Swap: 2000000 total, 1500000 used, 500000 free, 5000 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1234 www-data 20 0 500000 200000 10000 R 85.0 2.5 5:30.12 php-fpm 5678 www-data 20 0 500000 180000 10000 R 80.0 2.2 4:20.05 php-fpm 9012 mysql 20 0 1500000 500000 30000 S 20.0 6.2 10:00.00 mysqld
关键指标:
- • load average 高达15(你的CPU只有4核),远超正常值
这就是典型的“CPU打满 + 内存不足”导致服务器卡顿的现场。
原因分析
要理解为什么服务器会卡,我们需要从操作系统底层原理讲起。这里我用一个“厨房”的比喻帮你理解。
CPU——厨师
CPU就像厨房里的大厨,负责处理所有计算任务。每个进程(PHP、MySQL等)就像一道道菜。当同时来太多菜(进程数超过CPU核心数),大厨就得在菜之间频繁切换(上下文切换),切换本身也消耗时间。如果某个菜特别耗时间(比如PHP代码死循环),其他菜就得排队等着。
内存——备菜台
内存是厨师的操作台,上面放着各种食材和工具。如果操作台太小(内存不足),厨师就得频繁去仓库(磁盘)取东西。仓库取东西比从操作台拿慢1000倍以上(磁盘IO vs 内存访问)。
Swap——临时仓库
当备菜台放不下时,系统会把暂时不用的食材搬到仓库(Swap分区)。但仓库取东西极慢,频繁的Swap操作会导致系统像“死机”一样。
磁盘IO——传菜通道
磁盘IO就像传菜员,如果传菜通道被堵死(大量读写请求),厨师拿到食材的速度就会变慢,整个厨房效率下降。
网络——外卖窗口
网络是外卖窗口,如果窗口被堵住(大量连接),厨师做好菜也送不出去,导致请求堆积。
回到我们的例子:
- 1. PHP-FPM进程 占用了85%的CPU,说明PHP代码可能陷入死循环或执行了极慢的SQL查询
- 2. 内存 被大量消耗,Swap使用率75%,说明物理内存不够,系统在频繁换页
- 3. 磁盘IO 可能很高(多个进程在读写),进一步拖慢整体性能
这种“连锁反应”导致服务器响应越来越慢,最终“卡死”。
解决方案
下面我们分5个步骤来排查和解决。每个步骤都包含实际命令和操作。
步骤1:快速定位资源瓶颈
首先,用 top 或 htop 观察整体资源使用情况,然后用 vmstat 和 iostat 深入分析。
# 每2秒刷新一次,观察CPU、内存、IOvmstat 2 5# 输出示例(重点关注最后一列 wa):# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----# r b swpd free buff cache si so bi bo in cs us sy id wa st# 5 2 1500000 200000 300000 1500000 100 200 500 300 500 2000 95 3 0 2 0
关键指标解读:
- • r(running):等待CPU的进程数。如果 > CPU核心数,说明CPU是瓶颈
- • b(blocked):被IO阻塞的进程数。如果 > 0,说明磁盘IO有问题
- • si/so(swap in/out):如果持续 > 0,说明内存不足
- • wa(wait IO):CPU等待IO的时间。如果 > 10%,磁盘IO可能是瓶颈
# 查看磁盘IO详情iostat -x 2 5# 输出示例:# Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util# sda 0.00 0.00 100.00 200.00 1000.00 2000.00 20.00 5.00 20.00 10.00 25.00 2.00 60.00
关键指标:
- • await:平均IO等待时间(毫秒)。机械盘通常 < 10ms,SSD < 1ms
- • %util:磁盘利用率。接近100%说明磁盘饱和
💡 重要提示:如果 %util 接近100%但 await 不高,说明磁盘性能足够,是请求过多导致排队。如果 await 很高,说明磁盘本身慢(如老机械盘)。
步骤2:找出消耗CPU的进程
用 top 按 P 键(大写P)按CPU使用率排序。找到可疑进程后,用 strace 追踪系统调用:
# 假设PID 1234是消耗CPU最多的进程strace -p 1234 -c -S time 2>&1 | head -20# 输出示例:# % time seconds usecs/call calls errors syscall# ------ ----------- ----------- --------- --------- ----------------# 50.00 0.500000 1000 500 0 read# 30.00 0.300000 1500 200 0 write# 15.00 0.150000 3000 50 0 connect
如果发现 read 系统调用耗时最多,说明进程在等待IO(如慢SQL查询)。如果 connect 多,可能是网络请求阻塞。
另一个工具是 perf,可以采样分析CPU热点:
# 采样10秒perf record -g -p 1234 -- sleep 10perf report
💡 重要提示:strace 会降低进程性能,生产环境谨慎使用。可以先观察CPU占用的函数名,再决定是否追踪。
步骤3:检查内存和Swap使用
用 free -h 和 smem 查看内存详情:
free -h# total used free shared buff/cache available# Mem: 7.8G 5.9G 200M 100M 1.7G 1.2G# Swap: 2.0G 1.5G 500M# 查看每个进程的内存占用(按RSS排序)ps aux --sort=-%mem | head -10
如果Swap使用率 > 50%,说明物理内存严重不足。可以用 vmstat 观察 si/so 列是否持续 > 0。
临时缓解方案:
# 清空pagecache(谨慎使用,会降低后续性能)echo 1 > /proc/sys/vm/drop_caches# 或者清空dentries和inodesecho 2 > /proc/sys/vm/drop_caches# 同时清空echo 3 > /proc/sys/vm/drop_caches
⚠️ 警告:drop_caches 是临时措施,不会释放Swap。长期方案是增加物理内存或优化应用内存使用。
步骤4:检查磁盘IO和文件系统
用 iotop 实时查看哪个进程在读写磁盘:
# 需要root权限iotop -o -P# 输出示例:# Total DISK READ : 0.00 B/s | Total DISK WRITE : 2.00 M/s# Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 2.00 M/s# PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND# 1234 be/4 www-data 0.00 B/s 1.00 M/s 0.00 % 50.00 % php-fpm: pool www# 5678 be/4 mysql 0.00 B/s 1.00 M/s 0.00 % 30.00 % mysqld
如果发现某个进程IO很高,可以用 lsof 查看它打开了哪些文件:
lsof -p 1234
磁盘空间检查:
df -h# 如果某个分区使用率 > 90%,可能导致性能下降# 查找大文件du -sh /* 2>/dev/null | sort -rh | head -10
步骤5:检查网络连接和进程状态
用 netstat 或 ss 查看网络连接状态:
# 查看TCP连接状态统计ss -s# 输出示例:# Total: 1000 (kernel 1024)# TCP: 500 (estab 300, closed 100, orphaned 50, synrecv 10, timewait 40)# 查看具体连接ss -tan | awk '{print $1}' | sort | uniq -c# 300 ESTAB# 50 TIME-WAIT# 10 SYN-RECV
如果 SYN-RECV 数量很多,可能是遭受SYN洪水攻击。如果 TIME-WAIT 过多,需要调整内核参数。
检查进程状态:
# 查看D状态(不可中断睡眠)的进程ps aux | grep " D"
D状态进程通常表示等待磁盘IO完成。如果数量 > 5,磁盘IO很可能有问题。
对比不同方案的优缺点
| | | |
|---|
top | | | |
vmstat | | | |
iostat | | | |
strace | | | |
perf | | | |
iotop | | | |
ss | | | |
扩展知识
内核参数调优
如果频繁遇到连接数过多或TIME-WAIT堆积,可以调整以下内核参数:
# 快速回收TIME-WAIT连接echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.confecho "net.ipv4.tcp_tw_recycle = 1" >> /etc/sysctl.conf # 注意:NAT环境下慎用# 调整最大文件句柄数echo "fs.file-max = 655350" >> /etc/sysctl.conf# 应用配置sysctl -p
日志分析
卡顿问题往往在日志中有线索:
# 查看系统日志journalctl -xe --since "1 hour ago" | grep -i error# 查看应用日志(以Nginx为例)tail -f /var/log/nginx/error.log# 慢查询日志(MySQL)tail -f /var/log/mysql/slow-query.log
进阶学习建议
- 1. 性能分析工具集:学习使用
bcc(BPF Compiler Collection)工具,如 biosnoop、execsnoop 等 - 2. 内核源码:理解进程调度、内存管理、IO栈的底层实现
- 3. 监控系统:搭建Prometheus + Grafana,实现可视化监控和告警
- 4. 压测工具:掌握
ab、wrk、sysbench 等压测工具,主动发现性能瓶颈
互动话题
📞 凌晨的告警声,你最怕听到什么?
是CPU爆了、内存 leak,还是磁盘写满?
欢迎在评论区写下你经历过的最“刻骨铭心”的一次半夜救火,点赞最高的朋友,送一套《Linux运维命令地毯式排查表》电子版。
本文基于Linux内核5.10版本,CentOS 7/8和Ubuntu 20.04/22.04环境测试通过。不同发行版命令可能略有差异。