Linux 性能调优实战:从 top 到 perf 的完整工具链
性能问题的排查从来不是"运行一个命令",而是系统性地缩小问题范围。
方法论:USE 方法
每个资源(CPU、内存、IO、网络)都检查三个维度:
- Utilization:使用率(该资源有多忙?)
- Saturation:饱和度(是否有请求在排队等待?)
- Errors:错误(是否有操作失败?)
从宏观到微观:先确定瓶颈在哪个资源层,再深入该层排查。
CPU 分析
基础工具
# top:实时概览
# 关键指标:us(用户态)、sy(内核态)、wa(IO等待)、si(软中断)
top -H # 显示线程级别
# 负载均值的含义
# Load Average: 1.23, 0.87, 0.65 (1分钟, 5分钟, 15分钟)
# 单核 CPU:load=1 表示满载,>1 表示有任务在排队
# 4核 CPU:load=4 才是满载
# 经验法则:load / 核数 > 0.7 需要关注
nproc # 查看核数
uptime # 查看负载均值
# mpstat:多核 CPU 详情
apt install sysstat
mpstat -P ALL 1 # 每秒刷新,显示所有核
# 输出示例
# CPU %usr %sys %iowait %idle
# all 45.2 8.1 12.3 34.4
# 0 89.1 10.9 0.0 0.0 <- 某个核跑满了
# 上下文切换分析
vmstat 1
# r: 运行队列(等待 CPU 的进程数),持续 > 核数说明 CPU 紧张
# b: 阻塞进程数(等待 IO)
# cs: 每秒上下文切换次数,过高(>10万)说明线程太多
pidstat -w -p <PID> 1 # 进程级上下文切换
perf:性能分析利器
# 安装
apt install linux-perf
# 采样 CPU 热点(30秒)
perf record -g -F 99 -p <PID> sleep 30
perf report # 交互式查看
# 全系统热点
perf top -g # 实时显示热函数
# 统计特定事件
perf stat -e cache-misses,cache-references,instructions,cycles ./your_program
火焰图
# 1. 用 perf 采集
perf record -g -F 99 -p <PID> sleep 60
perf script > out.perf
# 2. 用 FlameGraph 生成 SVG
git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
./FlameGraph/flamegraph.pl out.folded > flamegraph.svg
# 宽度代表 CPU 时间占比,越宽越值得优化
内存分析
# free:内存使用概览
free -h
# Mem: total 16G, used 8G, free 2G, buff/cache 6G, available 8G
# "available" 才是实际可用(包含可回收的 cache),不是 free
# vmstat:虚拟内存统计
vmstat 1
# si/so: swap in/out(非零说明内存紧张,性能急剧下降)
# 进程内存详情
cat /proc/<PID>/smaps | grep -E "^(Pss|Rss|Size)" | \
awk '{sum[$1]+=$2} END{for(k in sum)print k, sum[k]/1024, "MB"}'
# RSS: 实际占用物理内存
# PSS: 按比例分配共享内存后的大小(更准确)
OOM Killer
# OOM 被触发时的日志
dmesg | grep -i "oom\|killed process"
journalctl -k | grep -i oom
# 输出示例:
# Out of memory: Kill process 12345 (java) score 892 or sacrifice child
# Killed process 12345 (java) total-vm:4096MB, anon-rss:3800MB
# 调整 OOM 优先级(-1000 到 1000,越高越先被杀)
echo 500 > /proc/<PID>/oom_score_adj # 提高被杀概率
echo -500 > /proc/<PID>/oom_score_adj # 降低被杀概率
内存泄漏定位
# valgrind(C/C++)
valgrind --leak-check=full --track-origins=yes ./program
# Python:tracemalloc
import tracemalloc
tracemalloc.start()
# ... 运行一段时间
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)
IO 分析
# iostat:磁盘 IO 统计
iostat -xz 1
# %util: 磁盘利用率(接近100%说明磁盘是瓶颈)
# await: 平均等待时间(ms),SSD 应 <1ms,HDD 应 <10ms
# r/s, w/s: 每秒读写次数(IOPS)
# iotop:进程级 IO
iotop -o # 只显示有 IO 的进程
iotop -a # 显示累计 IO 而非速率
# 打开文件
lsof -p <PID>
Page Cache 机制
# Linux 大量使用 Page Cache 加速 IO
cat /proc/meminfo | grep -E "Cached|Buffers|Dirty|Writeback"
# Dirty: 待写入磁盘的脏页
# Writeback: 正在写入磁盘的页
# 手动清理 cache(测试场景)
echo 3 > /proc/sys/vm/drop_caches
# 调整脏页刷新策略
sysctl -w vm.dirty_ratio=10
sysctl -w vm.dirty_background_ratio=5
网络分析
# ss:替代 netstat,更快
ss -tunap # 显示 TCP/UDP 连接
ss -s # 连接统计
# TIME_WAIT 大量堆积
ss -tan state time-wait | wc -l
# 解决方案
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_fin_timeout=30
# 网络吞吐监控
iftop -i eth0 # 实时带宽
nethogs # 进程级流量
TCP 状态机
CLOSED -> LISTEN -> SYN_RCVD -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED
|
CLOSE_WAIT -> LAST_ACK -> CLOSED
常见异常状态:
- 大量
CLOSE_WAIT:服务端没有正常关闭连接(代码 bug) - 大量
TIME_WAIT:短连接太多,考虑连接池 - 大量
SYN_RECV:可能遭受 SYN Flood 攻击
系统调用追踪
# strace:跟踪进程的系统调用
strace -p <PID>
strace -c ./program # 统计频次
strace -e trace=open,read,write ./program
strace -T ./program # 显示每个调用耗时
# ltrace:跟踪库函数调用
ltrace -p <PID>
ltrace -e malloc+free ./program
调优手段
sysctl 参数
# 网络优化
sysctl -w net.core.somaxconn=65535
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
# 持久化
echo "net.core.somaxconn=65535" >> /etc/sysctl.conf
sysctl -p
ulimit
ulimit -n 65535 # 文件描述符数量
# 持久化 /etc/security/limits.conf
echo "* soft nofile 65535" >> /etc/security/limits.conf
echo "* hard nofile 65535" >> /etc/security/limits.conf
CPU 亲和性
taskset -c 0,1 ./program # 绑定到 core 0 和 1
taskset -p -c 2,3 <PID> # 修改运行中进程
# NUMA 架构优化
numactl --cpunodebind=0 --membind=0 ./program
实际案例:iowait 高导致响应慢
现象: Web 服务响应时间从正常 50ms 飙升到 2s+,偶发性。
排查过程:
# Step 1: top 查看全局
# wa: 67% <- iowait 极高,IO 是瓶颈
# Step 2: iostat 定位磁盘
iostat -xz 1
# sda: %util 99.1%, await 234ms <- sda 磁盘几乎满载
# Step 3: iotop 定位进程
iotop -o
# 12345 java 123 MB/s 56 MB/s <- Java 进程疯狂写 IO
# Step 4: lsof 查看写的是什么文件
lsof -p 12345 | grep REG
# /var/log/app/app.log (size: 45GB) <- 日志文件巨大
# 根因:日志级别错误设置为 DEBUG,产生大量日志
# 解决:改回 INFO + 配置 logrotate + 使用异步日志
常用命令速查表
| 目的 | 命令 |
|---|---|
| CPU 总览 | top, htop |
| 多核详情 | mpstat -P ALL 1 |
| CPU 热点 | perf top -g |
| 内存概览 | free -h |
| IO 监控 | iostat -xz 1 |
| 进程 IO | iotop -o |
| 打开文件 | lsof -p <PID> |
| 网络连接 | ss -tunap |
| 带宽监控 | iftop -i eth0 |
| 系统调用 | strace -p <PID> |
| 负载历史 | sar -u 1 10 |
| 全局概览 | dstat -cdngy |
记住:性能调优是测量驱动的,不要凭直觉优化,先找到真正的瓶颈。