Linux 性能调优实战:从 top 到 perf 的完整工具链

遇到性能问题不知道从哪下手?这篇建立系统化的排查思路,从 CPU/内存/IO/网络逐层分析。

$1.5k 字/约 6 min👁— views

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

记住:性能调优是测量驱动的,不要凭直觉优化,先找到真正的瓶颈。