1. CPU 高怎么排查?

总览流程图

CPU 告警
  │
  ▼
top / htop 看整体
  │
  ├── us 高 → 用户态(业务代码)──→ 找进程 → 找线程 → 看调用栈
  ├── sy 高 → 内核态(系统调用)──→ 上下文切换?锁竞争?
  ├── wa 高 → 等 IO ──────────────→ 转磁盘/网络排查
  ├── si 高 → 软中断 ─────────────→ 网卡流量?定时器?
  └── st 高 → 被宿主机偷走 ───────→ 云平台问题

第一步:看整体,判断类型

$ top
%Cpu(s): 94.2 us,  2.1 sy,  0.0 ni,  3.5 id,  0.0 wa,  0.0 hi,  0.2 si
字段含义高时说明
us用户态业务代码在疯狂计算(死循环、正则、序列化等)
sy内核态大量系统调用、锁竞争、上下文切换
waIO 等待本质不是 CPU 问题,是磁盘/网络慢
si软中断网卡收包多、定时器密集
st被偷走虚拟机被宿主机抢占,找云平台

💡 先判断类型,再走对应分支,不要盲目找进程。


第二步:分支处理


分支 A:us 高(最常见,占 80% 场景)

逐层定位:进程 → 线程 → 函数/代码行

① 找进程
② 找线程
③ 看调用栈
④ 定位代码

① 找进程

$ top          # 按 P 排序(按 %CPU 降序)
  PID USER   %CPU  %MEM  COMMAND
28467 app    380.0  40.1  java

② 找线程

$ top -Hp 28467    # 查看该进程下所有线程
  TID   %CPU  COMMAND
28489   99.8  java
28490   99.7  java

③ 看调用栈

根据语言选择工具:

语言工具命令
Javajstackprintf "0x%x\n" 28489 → jstack 28467 | grep -A30 "0x6f59"
C/C++perf / gdbperf top -p 28467 或 gdb -p 28467 → thread apply all bt
Gopprofcurl http://localhost:6060/debug/pprof/profile
Pythonpy-spypy-spy top --pid 28467
Node.js--prof / clinicnode --prof app.js → node --prof-process
通用perfperf top -p 28467 / perf record -g -p 28467

④ 定位代码并修复

"pool-3-thread-1" nid=0x6f59 runnable
   java.lang.Thread.State: RUNNABLE
     at com.example.service.OrderService.calculateDiscount(OrderService.java:128)  ← 就是这里

分支 B:sy 高

怀疑方向:上下文切换过多 / 锁竞争 / 系统调用频繁

# 看上下文切换
$ vmstat 1 5
 r  b   swpd   free  ...   in      cs     us sy id wa st
48  0      0  12048  ...  312  485621    12 38 49  0  1
指标正常异常
cs(上下文切换)几千~几万>10万
r(运行队列)≤ 核数远大于核数

如果 cs 异常高:

# 看是不是线程太多
$ ps -eLf | wc -l
​
# 看是不是锁竞争(Java)
$ jstack <PID> | grep -c "BLOCKED"
​
# 看系统调用热点
$ perf top -e syscalls:sys_enter_* -p <PID>
​
# 或者用 strace 统计系统调用
$ strace -c -p <PID>
常见原因解法
线程池设太大缩小线程池
锁粒度太粗分段锁 / 无锁结构
大量短生命周期线程复用线程池
频繁 futex 调用检查锁竞争

分支 C:wa 高

这不是 CPU 问题,是 IO 问题。

# 看哪个磁盘慢
$ iostat -x 1
​
# 看哪个进程在等 IO
$ iotop -oP

转去排查磁盘/网络瓶颈。


分支 D:si 高
# 看软中断分布
$ cat /proc/softirqs
​
# 看网卡流量
$ sar -n DEV 1
​
# 看是不是单核打满(中断亲和性问题)
$ mpstat -P ALL 1
常见原因解法
网卡流量太大限流 / 扩容
中断都集中在一个核配置 irqbalance 或手动绑核
定时器太密集检查 timer 相关软中断

分支 E:st 高

你的 CPU 被宿主机/其他虚拟机偷走了。

$ top   # 看 st 列
%Cpu(s):  5.0 us,  1.0 sy,  0.0 id,  0.0 wa,  0.0 st
# st 持续 > 5% → 找云平台

解法:联系云平台 / 换机型 / 升级配置。


第三步:常见根因速查表

根因特征定位方法
死循环某线程 100% 且栈不变jstack 多次抓取对比
正则回溯us 高,栈在 java.util.regexjstack 看调用栈
频繁 GCus 高,GC 日志频繁jstat -gcutil <PID> 1000
序列化/反序列化us 高,栈在 JSON/Protobufperf top
锁竞争sy 高,大量 BLOCKEDjstack / vmstat
线程过多sy 高,cs 极高ps -eLf | wc -l
加密运算us 高,栈在 SSL/AESperf top
内存泄漏导致 GCus 高 + 内存持续增长jmap -histo / MAT

第四步:修复 & 验证

# 修复后观察
$ top            # CPU 是否下降
$ vmstat 1       # cs 是否正常
$ jstat -gcutil <PID> 1000   # GC 是否正常(Java)

2. 内存怎么看?

核心命令:free -h

              total    used    free   shared  buff/cache   available
Mem:           15Gi    8Gi   1.2Gi   512Mi       6Gi        6.5Gi
Swap:         4.0Gi   200Mi   3.8Gi

关键理解:

字段含义
used进程实际使用的内存
buff/cache内核缓冲区 + 页缓存(可回收,不算"真占用")
available估算的可用内存(free + 可回收的 cache)

判断标准:

  • 看 available 而非 free。available 低才是真缺内存

  • Swap 使用量持续上升 → 内存压力大

进一步排查:

# 看进程内存排序
ps aux --sort=-%mem | head -10
​
# 看详细内存分布
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Cached|Buffers|Slab"
​
# 看某进程内存明细
cat /proc/<PID>/smaps_rollup
pmap -x <PID>

内存泄漏特征: 进程 RSS 持续增长不回落,可用 valgrind 或 jemalloc 的 heap profiling 定位。


3. Load Average 是什么?

定义: 单位时间内处于 可运行状态(R) 和 不可中断睡眠状态(D) 的进程/线程的平均数。

uptime
# 14:30:01 up 30 days, load average: 2.50, 3.10, 4.20
#                                    1min    5min   15min

三个值含义:

  • 1 min:最近 1 分钟的平均负载(反映瞬时压力)

  • 5 min:最近 5 分钟(反映短期趋势)

  • 15 min:最近 15 分钟(反映长期趋势)

如何判断是否过载:

场景判断
Load < CPU 核数正常,有空闲
Load ≈ CPU 核数满负荷
Load > CPU 核数 × 1.5过载,需要关注
Load >> CPU 核数严重过载

⚠️ 注意:Load 高不一定是 CPU 高。大量 D 状态(磁盘 IO 等待)的进程也会拉高 Load。

# 查看 D 状态进程
ps aux | awk '$8=="D" {print}'

4. df 和 du 有什么区别?

对比项dfdu
视角文件系统(磁盘分区)级别目录/文件级别
数据来源读取文件系统超级块(superblock)遍历目录树逐个统计
速度快(直接读元数据)慢(递归遍历)
包含已删除文件✅ 包含(被进程持有的已删除文件仍占空间)❌ 不包含

经典不一致场景:

df -h /data    # 显示 90% 已满
du -sh /data   # 显示只有 50G

原因: 有进程打开了已删除(rm)的文件,文件描述符未释放,空间未归还文件系统。

排查方法:

# 找到被删除但仍被占用的文件
lsof | grep deleted
​
# 释放方法:重启对应进程,或清空文件描述符
> /proc/<PID>/fd/<FD>

5. inode 用完是什么现象?

典型表现:

No space left on device

但用 df -h 看磁盘空间明明还有剩余!

验证:

df -i    # 查看 inode 使用率
# Filesystem     Inodes  IUsed   IFree IUse% Mounted on
# /dev/sda1      655360 655360      0  100% /

根因: 磁盘空间是按"块"分配的,但每个文件/目录至少要占一个 inode。大量小文件(如海量日志、临时文件、邮件队列)会耗尽 inode。

排查步骤:

# 1. 确认 inode 满了
df -i
​
# 2. 找出哪个目录文件数最多
for d in /var /tmp /home /opt; do
  echo "$(find $d -xdev | wc -l) $d"
done | sort -rn
​
# 3. 进一步下钻
find /var/spool -type f | wc -l

常见场景:

  • /var/spool/postfix 邮件堆积

  • /tmp 或 /var/tmp 大量小临时文件

  • 定时任务产生海量小日志文件

  • 容器层(overlay)产生大量碎片文件

解决方案:

  • 清理无用小文件

  • 格式化时指定更多 inode:mkfs.ext4 -N <数量>

  • 使用 XFS(inode 动态分配,不会预先耗尽)


总结速查表

问题第一反应命令
CPU 高top → top -Hp → perf
内存不足free -h → ps aux --sort=-%mem
Load 高uptime + `ps aux
磁盘满但 du 对不上df -h vs du -sh + `lsof
有空间却写不了文件df -i 查 inode
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐