Linux系统性能排查实战指南:从定位到解决
前言
上周线上服务器突然变慢,用户反馈接口响应从200ms飙到2s。
登上服务器一看,load average飙到20多(4核机器),但具体是什么原因导致的?CPU?内存?磁盘IO?网络?
花了2小时才定位到问题:一个定时任务写日志把磁盘写满了,导致数据库疯狂刷盘。
这篇文章把性能排查的完整流程和工具总结一下,下次再遇到类似问题能快速定位。
一、性能问题排查思路
遇到性能问题,不要慌,按这个顺序排查:
- 先看整体负载(load average)
↓ - 定位瓶颈类型(CPU/内存/磁盘/网络)
↓ - 找到问题进程
↓ - 分析根因
↓ - 解决问题
**核心原则:从宏观到微观,从现象到本质。**
---
## 二、第一步:看整体负载
### 2.1 uptime - 快速查看负载
```bash
$ uptime
14:30:25 up 45 days, 3:21, 2 users, load average: 8.52, 6.31, 4.12
load average三个数字含义:
- 第一个:最近1分钟平均负载
- 第二个:最近5分钟平均负载
- 第三个:最近15分钟平均负载
怎么判断负载高不高?
对比CPU核心数:
# 查看CPU核心数
$ nproc
4
- load average < CPU核心数 → 正常
- load average ≈ CPU核心数 → 较忙
- load average > CPU核心数 × 2 → 过载,需要排查
上面的例子,4核机器load到8.52,已经过载了。
2.2 top - 实时监控
$ top
关注几个关键指标:
top - 14:30:25 up 45 days, 3:21, 2 users, load average: 8.52, 6.31, 4.12
Tasks: 203 total, 3 running, 200 sleeping, 0 stopped, 0 zombie
%Cpu(s): 75.2 us, 12.3 sy, 0.0 ni, 8.5 id, 3.2 wa, 0.0 hi, 0.8 si, 0.0 st
MiB Mem : 7821.3 total, 234.5 free, 6543.2 used, 1043.6 buff/cache
MiB Swap: 2048.0 total, 1024.0 free, 1024.0 used. 876.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12345 mysql 20 0 4523412 3.2g 8532 S 156.3 42.1 1234:56 mysqld
23456 www-data 20 0 892340 256m 12340 S 45.2 3.3 567:89 php-fpm
重点看这几行:
| 指标 | 含义 | 异常判断 |
|---|---|---|
us |
用户态CPU | >70% 可能有CPU密集任务 |
sy |
内核态CPU | >30% 可能有大量系统调用 |
wa |
IO等待 | >20% 说明磁盘IO有瓶颈 |
id |
空闲 | 越低越忙 |
Mem used |
内存使用 | 接近total需要关注 |
Swap used |
交换分区 | 有使用说明内存不够 |
2.3 htop - 更直观的top
# 安装
apt install htop # Ubuntu
yum install htop # CentOS
# 运行
htop
htop相比top的优势:
- 彩色显示,更直观
- 可以鼠标操作
- 可以直接kill进程
- 支持树状显示进程父子关系
三、CPU问题排查
3.1 判断是否CPU瓶颈
$ top
# 看 %Cpu(s) 行的 us + sy 是否接近100%
# 看 id(空闲)是否接近0%
如果 us + sy > 90% 且 id < 10%,基本可以确定是CPU瓶颈。
3.2 找到CPU高的进程
# 按CPU排序(top默认就是)
$ top -o %CPU
# 或者用ps
$ ps aux --sort=-%cpu | head -20
3.3 分析进程在干什么
找到高CPU进程后,分析它在做什么:
方法1:strace 跟踪系统调用
# 跟踪进程的系统调用
$ strace -p <PID> -c
^C
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
85.23 1.234567 123 10000 write
8.45 0.123456 12 10000 read
4.32 0.054321 5 10000 open
方法2:perf 分析CPU热点
# 安装
apt install linux-tools-generic
# 采样30秒
$ perf record -p <PID> -g -- sleep 30
# 查看结果
$ perf report
方法3:看进程的堆栈
# Java进程
$ jstack <PID>
# 通用方法
$ cat /proc/<PID>/stack
3.4 常见CPU高的原因
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 单个进程CPU 100% | 死循环、算法问题 | 代码review、优化算法 |
| 多个进程CPU都高 | 并发请求过多 | 限流、扩容 |
| sy(系统态)高 | 大量系统调用 | 减少IO操作、优化代码 |
| 短时间CPU飙高 | 定时任务、GC | 错峰执行、调优GC |
四、内存问题排查
4.1 查看内存使用
$ free -h
total used free shared buff/cache available
Mem: 7.6Gi 5.8Gi 234Mi 123Mi 1.6Gi 1.4Gi
Swap: 2.0Gi 1.0Gi 1.0Gi
重点指标:
available:真正可用的内存(比free更准确)Swap used:如果不为0,说明物理内存不够了
4.2 找到内存大户
# 按内存排序
$ ps aux --sort=-%mem | head -20
# 或者用top,按M键切换为内存排序
$ top
# 然后按 M
4.3 分析内存去哪了
系统层面:
# 查看内存详细分布
$ cat /proc/meminfo
# 重点看这几项
MemTotal: 8000000 kB # 总内存
MemFree: 200000 kB # 空闲内存
MemAvailable: 1500000 kB # 可用内存
Buffers: 100000 kB # 缓冲区
Cached: 1200000 kB # 页面缓存
SwapTotal: 2000000 kB # 交换分区总量
SwapFree: 1000000 kB # 交换分区空闲
进程层面:
# 查看进程内存详情
$ cat /proc/<PID>/status | grep -E "VmRSS|VmSize"
VmSize: 4523412 kB # 虚拟内存(不重要)
VmRSS: 3321456 kB # 实际物理内存(重要)
# 查看进程内存映射
$ cat /proc/<PID>/smaps | head -50
4.4 内存泄漏排查
如果发现某个进程内存持续增长:
# 定时记录内存使用
$ while true; do
echo "$(date) $(ps -p <PID> -o rss=)" >> /tmp/mem.log
sleep 60
done
# 一段时间后分析增长曲线
Java进程:
# 查看堆内存
$ jmap -heap <PID>
# dump堆内存分析
$ jmap -dump:format=b,file=heap.bin <PID>
# 用MAT或VisualVM分析
4.5 OOM Killer
如果内存真的不够,Linux会启动OOM Killer杀进程:
# 查看被OOM杀掉的进程
$ dmesg | grep -i "out of memory"
$ dmesg | grep -i "killed process"
五、磁盘IO问题排查
5.1 判断是否IO瓶颈
$ top
# 看 wa(IO wait)是否 > 20%
如果wa很高,基本就是磁盘IO瓶颈。
5.2 iostat - 磁盘IO监控
# 安装
apt install sysstat
# 每2秒刷新一次
$ iostat -xz 2
输出解读:
Device r/s w/s rkB/s wkB/s %util
sda 15.00 250.00 120.00 51200.00 98.50
| 指标 | 含义 | 异常判断 |
|---|---|---|
| r/s | 每秒读次数 | - |
| w/s | 每秒写次数 | - |
| rkB/s | 每秒读取KB | - |
| wkB/s | 每秒写入KB | - |
| %util | 磁盘繁忙度 | >80% 说明磁盘繁忙 |
5.3 找到IO高的进程
# iotop需要安装
$ apt install iotop
# 查看IO最高的进程
$ iotop -o
输出:
PID PRIO USER DISK READ DISK WRITE COMMAND
12345 be/4 mysql 0.00 B/s 50.23 M/s mysqld
23456 be/4 root 0.00 B/s 10.45 M/s rsync
5.4 磁盘空间检查
# 查看磁盘使用率
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 100G 95G 5G 95% /
# 查看哪个目录占用大
$ du -sh /* | sort -rh | head -20
# 更细的分析
$ du -sh /var/* | sort -rh | head -10
磁盘满了会导致:
- 数据库无法写入
- 日志无法记录
- 应用崩溃
5.5 常见IO问题
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| %util接近100% | 磁盘瓶颈 | 换SSD、优化查询 |
| 磁盘空间满 | 日志太多、大文件 | 清理日志、扩容 |
| 随机IO高 | 数据库查询慢 | 加索引、优化SQL |
| 顺序IO高 | 大文件读写 | 错峰执行 |
六、网络问题排查
6.1 查看网络连接
# 查看所有连接
$ ss -tunap
# 统计各状态连接数
$ ss -s
Total: 1234
TCP: 890 (estab 567, closed 123, orphaned 0, timewait 200)
# 查看TIME_WAIT连接数
$ ss -tan | grep TIME-WAIT | wc -l
TIME_WAIT过多会导致端口耗尽:
# 查看可用端口范围
$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 60999
# 如果TIME_WAIT接近这个范围,需要优化
6.2 网络流量监控
# 安装iftop
$ apt install iftop
# 查看实时流量
$ iftop -i eth0
或者用更简单的:
# 每秒刷新
$ watch -n 1 "cat /proc/net/dev | grep eth0"
6.3 抓包分析
# 抓取特定端口的包
$ tcpdump -i eth0 port 3306 -w mysql.pcap
# 抓取特定IP的包
$ tcpdump -i eth0 host 192.168.1.100
# 用Wireshark分析
6.4 网络延迟排查
# ping测试
$ ping -c 10 目标IP
# 路由追踪
$ traceroute 目标IP
# 更详细的mtr
$ mtr 目标IP
七、实战案例
案例1:接口变慢,CPU正常,wa高
现象:
- 接口响应从200ms变成2s
- top看CPU的wa(IO等待)达到40%
- load average很高
排查过程:
# 1. 确认是IO问题
$ iostat -xz 2
# 发现 %util 接近100%
# 2. 找到IO高的进程
$ iotop -o
# 发现是mysql写入很高
# 3. 查看磁盘空间
$ df -h
# 发现根分区使用率99%!
# 4. 找到大文件
$ du -sh /var/* | sort -rh
# /var/log 占了80G
# 5. 具体是哪个日志
$ ls -lh /var/log/*.log | sort -k5 -h
# 发现某个应用日志有60G
解决:
- 清理日志:
> /var/log/xxx.log - 配置日志轮转:logrotate
- 监控磁盘使用率告警
案例2:内存持续增长,最终OOM
现象:
- 应用每隔几天就会被OOM Killer杀掉
- 重启后又正常,过几天又OOM
排查过程:
# 1. 确认是哪个进程被杀
$ dmesg | grep -i "killed process"
# 发现是java进程
# 2. 监控内存增长
$ while true; do
ps -p <PID> -o rss= >> /tmp/mem.log
sleep 300
done
# 发现内存每天增长几百MB
# 3. dump堆内存
$ jmap -dump:format=b,file=heap.bin <PID>
# 用MAT分析,发现某个Map一直在增长
# 4. 代码review
# 发现是缓存没有设置过期时间,一直往里塞数据
解决:
- 缓存设置过期时间和最大容量
- 定期重启(临时方案)
- 增加内存监控告警
案例3:偶发性卡顿
现象:
- 每天固定时间(凌晨2点)系统变慢
- 其他时间正常
排查:
# 查看定时任务
$ crontab -l
0 2 * * * /opt/backup.sh
# 查看备份脚本
$ cat /opt/backup.sh
# 发现是全量数据库备份,占用大量IO
解决:
- 备份任务加nice降低优先级
- 使用增量备份
- 错峰到业务低谷期
八、性能排查工具速查表
| 类别 | 工具 | 用途 |
|---|---|---|
| 综合 | top/htop | 实时监控CPU、内存 |
| vmstat | 系统整体状态 | |
| dstat | 综合资源监控 | |
| CPU | top | 查看CPU使用 |
| perf | CPU热点分析 | |
| strace | 系统调用跟踪 | |
| 内存 | free | 内存使用概览 |
| ps aux --sort=-%mem | 内存大户 | |
| pmap | 进程内存映射 | |
| 磁盘 | df | 磁盘空间 |
| du | 目录大小 | |
| iostat | 磁盘IO | |
| iotop | IO进程排序 | |
| 网络 | ss/netstat | 连接状态 |
| iftop | 流量监控 | |
| tcpdump | 抓包分析 |
九、一键排查脚本
分享一个我常用的快速排查脚本:
#!/bin/bash
# quick-check.sh - 快速性能检查
echo "========== 系统负载 =========="
uptime
echo -e "\n========== CPU使用 =========="
top -bn1 | head -5
echo -e "\n========== 内存使用 =========="
free -h
echo -e "\n========== 磁盘空间 =========="
df -h | grep -v tmpfs
echo -e "\n========== 磁盘IO =========="
iostat -x 1 2 | tail -10
echo -e "\n========== CPU占用TOP10 =========="
ps aux --sort=-%cpu | head -11
echo -e "\n========== 内存占用TOP10 =========="
ps aux --sort=-%mem | head -11
echo -e "\n========== 网络连接统计 =========="
ss -s
echo -e "\n========== 最近的系统日志 =========="
dmesg | tail -20
chmod +x quick-check.sh
./quick-check.sh
十、远程服务器排查技巧
如果你管理的服务器分布在不同地方(公司机房、云服务器、家里的服务器),远程排查会遇到一些额外的问题。
10.1 SSH连接慢或断开
服务器负载高时,SSH可能连不上或者很卡。
解决方案:
- 用mosh替代SSH(更抗网络波动):
# 服务器安装
apt install mosh
# 客户端连接
mosh user@server
- 开启SSH保活:
# ~/.ssh/config
Host *
ServerAliveInterval 30
ServerAliveCountMax 5
10.2 多台服务器统一管理
服务器多了,一台台登录很麻烦。
方案1:使用tmux/screen保持会话
# 服务器上启动tmux
tmux new -s debug
# 断开后重连
tmux attach -t debug
方案2:批量执行命令
# 使用pdsh并行执行
pdsh -w server[1-10] "uptime"
# 或者用ansible
ansible all -m shell -a "uptime"
10.3 跨网络访问内网服务器
有时候服务器在内网,没有公网IP,远程排查就很麻烦。
比如:
- 家里的开发服务器,在公司想连上去排查问题
- 客户的服务器在内网,需要远程协助排查
传统方案:
- VPN:配置复杂,维护成本高
- 跳板机:多一跳,延迟高
- frp内网穿透:需要自己维护服务端
我的方案:用星空组网
原理是把多台服务器组成一个虚拟局域网,不管在哪都能直接SSH连接。
# 1. 服务器和本地电脑都装星空组网客户端
# 2. 登录同一账号,加入同一网络
# 3. 服务器获得虚拟IP,比如 10.26.1.10
# 直接用虚拟IP SSH连接
ssh user@10.26.1.10
# 或者配置~/.ssh/config
Host homeserver
HostName 10.26.1.10
User root
优势:
- P2P直连,延迟低(比跳板机快)
- 不需要公网IP
- 免费版3设备够用
- 配置5分钟搞定
我现在管理的几台服务器(家里、公司、云上)都组在一个网络里,在哪都能直接连,排查问题方便多了。
10.4 远程排查的注意事项
- 先确认能稳定连接,再开始排查(别排查到一半断了)
- 操作前做好记录,方便事后复盘
- 高危操作用screen/tmux,防止SSH断开导致命令中断
- 修改配置前先备份:
cp config config.bak
十一、总结
性能排查的核心思路:
- 先看整体:uptime看负载,top看资源分布
- 定位瓶颈类型:是CPU、内存、磁盘还是网络?
- 找到问题进程:用对应工具排序
- 分析根因:看日志、抓包、分析堆栈
- 解决问题:优化代码、调整配置、扩容
记住几个关键阈值:
- load average > CPU核心数×2 → 过载
- CPU wa > 20% → IO瓶颈
- 内存available接近0 → 内存不足
- 磁盘%util > 80% → 磁盘繁忙
- 磁盘使用率 > 90% → 需要清理
有问题评论区讨论,这些都是踩坑总结出来的~
我的环境:
- Ubuntu 22.04 / CentOS 7
- 常用工具版本:sysstat 12.x、iotop 0.6

浙公网安备 33010602011771号