Linux系统性能排查实战指南:从定位到解决

前言

上周线上服务器突然变慢,用户反馈接口响应从200ms飙到2s。

登上服务器一看,load average飙到20多(4核机器),但具体是什么原因导致的?CPU?内存?磁盘IO?网络?

花了2小时才定位到问题:一个定时任务写日志把磁盘写满了,导致数据库疯狂刷盘。

这篇文章把性能排查的完整流程和工具总结一下,下次再遇到类似问题能快速定位。


一、性能问题排查思路

遇到性能问题,不要慌,按这个顺序排查:


  1. 先看整体负载(load average)
  2. 定位瓶颈类型(CPU/内存/磁盘/网络)
  3. 找到问题进程
  4. 分析根因
  5. 解决问题
**核心原则:从宏观到微观,从现象到本质。**

---

## 二、第一步:看整体负载

### 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

解决:

  1. 清理日志:> /var/log/xxx.log
  2. 配置日志轮转:logrotate
  3. 监控磁盘使用率告警

案例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
# 发现是缓存没有设置过期时间,一直往里塞数据

解决:

  1. 缓存设置过期时间和最大容量
  2. 定期重启(临时方案)
  3. 增加内存监控告警

案例3:偶发性卡顿

现象:

  • 每天固定时间(凌晨2点)系统变慢
  • 其他时间正常

排查:

# 查看定时任务
$ crontab -l
0 2 * * * /opt/backup.sh

# 查看备份脚本
$ cat /opt/backup.sh
# 发现是全量数据库备份,占用大量IO

解决:

  1. 备份任务加nice降低优先级
  2. 使用增量备份
  3. 错峰到业务低谷期

八、性能排查工具速查表

类别 工具 用途
综合 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可能连不上或者很卡。

解决方案:

  1. 用mosh替代SSH(更抗网络波动):
# 服务器安装
apt install mosh

# 客户端连接
mosh user@server
  1. 开启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 远程排查的注意事项

  1. 先确认能稳定连接,再开始排查(别排查到一半断了)
  2. 操作前做好记录,方便事后复盘
  3. 高危操作用screen/tmux,防止SSH断开导致命令中断
  4. 修改配置前先备份cp config config.bak

十一、总结

性能排查的核心思路:

  1. 先看整体:uptime看负载,top看资源分布
  2. 定位瓶颈类型:是CPU、内存、磁盘还是网络?
  3. 找到问题进程:用对应工具排序
  4. 分析根因:看日志、抓包、分析堆栈
  5. 解决问题:优化代码、调整配置、扩容

记住几个关键阈值:

  • load average > CPU核心数×2 → 过载
  • CPU wa > 20% → IO瓶颈
  • 内存available接近0 → 内存不足
  • 磁盘%util > 80% → 磁盘繁忙
  • 磁盘使用率 > 90% → 需要清理

有问题评论区讨论,这些都是踩坑总结出来的~


我的环境:

  • Ubuntu 22.04 / CentOS 7
  • 常用工具版本:sysstat 12.x、iotop 0.6

posted @ 2025-12-02 15:13  花宝宝  阅读(0)  评论(0)    收藏  举报