性能问题通常表现为 CPU 高、内存不足、磁盘变慢或网络吞吐异常。与其凭感觉猜测,不如用系统提供的工具先采集数据,再定位瓶颈。

本文整理一套从“整体负载”到“单项指标”的工具链:

  • 系统级:uptime、dmesg、vmstat、mpstat、pidstat、iostat、free、sar、top
  • 程序级:/usr/bin/time、gprof
  • 专项测试:fio(磁盘)、iperf3(网络)

以下命令基于 Linux 环境,部分工具来自 sysstat 软件包:

sudo apt install sysstat iperf3 fio gprofng gcc

一、性能分析思路

性能分析通常遵循“先整体、后局部”的顺序:

  1. 确认问题表现:是响应慢、吞吐低,还是系统不稳定。
  2. 采集整体指标:负载、CPU、内存、磁盘、网络。
  3. 缩小范围:按进程、按 CPU、按磁盘设备逐步定位。
  4. 深入分析:使用剖析工具或专项测试复现。

Brendan Gregg 在《Systems Performance》中提出的 USE 方法也很常用:

对于每个资源,检查使用率(Utilization)、饱和度(Saturation)和错误(Errors)。

本文的 60 秒检查清单正是这套思路的命令化实现。

二、Linux 60 秒检查清单

下面的命令按顺序执行,可以在 60 秒内快速了解一台机器的健康状态。

2.1 uptime:负载均值

uptime

load average 显示 1、5、15 分钟的平均负载。负载可以简单理解为可运行进程与不可中断进程的平均数量,需要结合 CPU 核数判断:

  • 负载持续接近 CPU 核数,说明 CPU 接近饱和。
  • 1 分钟负载明显高于 15 分钟,说明负载正在上升。

2.2 dmesg:内核错误与 OOM

dmesg -T | tail

-T 将时间戳转换为可读时间。重点查看:

  • Out of memory:内存耗尽导致进程被杀。
  • blocked for more than 120 seconds:进程长时间等待 IO。
  • 硬件错误、驱动异常等内核日志。

2.3 vmstat:系统整体指标

vmstat -SM 1

每秒输出一行,-SM 以 MB 为单位。关键字段:

字段 含义
r 等待运行的进程数
b 不可中断睡眠的进程数(通常是磁盘 IO)
swpd 已使用的交换空间
si / so 每秒从磁盘换入 / 换出到磁盘的内存
bi / bo 块设备每秒接收 / 发送的块数
in / cs 每秒中断数 / 上下文切换数
us / sy 用户态 / 内核态 CPU 时间占比
wa 等待 IO 的 CPU 时间占比
st 虚拟机被宿主机抢占的时间占比

如果 si、so 持续非零,说明内存紧张,系统正在频繁交换。

2.4 mpstat:各 CPU 使用情况

mpstat -P ALL 1

查看每个 CPU 的使用率,检查程序是否只跑在一个核上。单个 CPU 饱和而其他 CPU 空闲,说明程序扩展性差,可能是单线程热点或锁竞争。

2.5 pidstat:按进程统计 CPU

pidstat 1

每秒输出各进程的 CPU 占用,找出意外的 CPU 消费者。也可以按进程名过滤:

pidstat -p $(pgrep -f app_name | head -1) 1

2.6 iostat:磁盘 IO

iostat -sxz 1

-s 省略设备名重复显示,-x 显示扩展信息,-z 忽略空闲设备。关键字段:

字段 含义
tps 每秒传输次数(IOPS)
rqm/s 每秒合并的请求数
await IO 请求平均等待时间(毫秒)
%util 设备忙占比,接近 100% 表示设备接近饱和

注意 %util 高不等于设备慢,还要结合 await 判断单次请求耗时。

2.7 free:内存

free -m

Linux 会把空闲内存用于页缓存,因此 free 列很小不代表内存不足。更值得关注的是:

  • available:可用于新程序的内存。
  • swap 使用量:持续增长说明内存压力大。

2.8 sar:网络与 TCP

sar -n DEV 1
sar -n TCP,ETCP 1

-n DEV 显示各网卡的收发字节与数据包速率,-n TCP,ETCP 显示 TCP 连接状态和错误:

  • retrans 重传次数持续增长,说明网络存在丢包或拥塞。
  • active/s、passive/s 是每秒主动 / 被动建立的连接数。

2.9 top:总览

top

top 整合了进程 CPU、内存、负载和运行队列,适合交互式查看。批处理模式下可以单次采样:

top -bn1 | head -30

三、程序耗时与资源统计:time

/usr/bin/time 可以统计程序运行时间、内存和系统资源。注意 shell 内置的 time 输出格式有限,这里使用外部程序版本:

/usr/bin/time -v ./my_program

输出示例与关键字段:

User time (seconds): 0.00          # 用户态耗时
System time (seconds): 0.00        # 内核态耗时
Percent of CPU this job got: 18%   # CPU 占比
Elapsed (wall clock) time: 0:00.04 # 实际经过时间
Maximum resident set size (kbytes): 920   # 峰值内存(RSS)
Major (requiring I/O) page faults: 0      # 主缺页:需要从磁盘读回
Minor (reclaiming a frame) page faults: 139  # 次缺页:可复用内存页
Voluntary context switches: 292           # 自愿上下文切换
Involuntary context switches: 0           # 非自愿上下文切换
  • User time 远大于 System time:CPU 密集,优化方向是算法和缓存。
  • System time 占比高:大量系统调用或内核工作。
  • Major page faults 多:内存访问模式差或内存不足。
  • RSS 用于判断程序峰值内存需求。

四、CPU 剖析:gprof

gprof 通过编译期插桩统计函数调用次数和耗时,适合开发阶段定位热点函数。

g++ -pg -O0 -g -o my_program my_program.cpp
./my_program
gprof my_program gmon.out > analysis.txt

要点:

  • 必须使用 -pg 重新编译,并在函数入口插入计数代码。
  • 运行结束后生成 gmon.out,用 gprof 生成报告。
  • 优化级别会影响统计结果,建议先用 -O0 分析。
  • gprof 只能统计被插桩覆盖的函数,动态加载的库无法完整统计。

新版本的 GCC 还提供 gprofng 作为替代方案:

gprofng collect app
gprofng display text app.er.1

五、采样剖析:perf 与火焰图

gprof 需要重新编译,不适合分析线上程序。perf 是 Linux 内核自带的采样型剖析工具,通过周期采样记录调用栈,不需要修改程序,生产环境也可使用。

5.1 安装与权限

sudo apt install linux-tools-common linux-tools-generic
# 或按内核版本安装
sudo apt install linux-tools-$(uname -r)

采样依赖性能事件,权限由内核参数 perf_event_paranoid 控制:

cat /proc/sys/kernel/perf_event_paranoid

值为 2 时只能采样用户态事件,需要内核事件时可以临时放宽:

sudo sysctl kernel.perf_event_paranoid=1

5.2 采集 CPU 热点

# 以 99Hz 采样全部 CPU 10 秒
perf record -g -a -F 99 sleep 10

# 采样指定进程
perf record -g -p <pid> sleep 10

# 查看报告
perf report

常用参数:

  • -g:记录调用栈。
  • -F 99:采样频率,99Hz 是常用值。
  • -a:采集所有 CPU。
  • -p <pid>:只采集指定进程。

perf top 可以实时查看热点函数:

perf top

5.3 生成火焰图

火焰图(Flame Graph)由 Brendan Gregg 提出,把采样到的调用栈按堆叠方式可视化。x 轴方向越宽说明该函数占用 CPU 时间越多,y 轴是调用栈深度。

使用 FlameGraph 工具生成 SVG:

git clone https://github.com/brendangregg/FlameGraph
cd FlameGraph

perf script > out.perf
./stackcollapse-perf.pl out.perf > out.folded
./flamegraph.pl out.folded > flamegraph.svg

也可以一条命令完成:

perf script -i perf.data | ./stackcollapse-perf.pl | ./flamegraph.pl > flamegraph.svg

用浏览器打开 flamegraph.svg,宽大的顶部方块通常就是优化重点。

5.4 perf 与 gprof 对比

特性 perf gprof
原理 采样 编译插桩
是否重新编译 不需要 必须加 -pg
适合环境 生产环境 开发环境
运行时开销 较低 较高
函数调用次数 不统计 统计

5.5 其他常用功能

# 统计系统调用次数
perf stat -e syscalls:sys_enter_* -a sleep 5

# 采集缓存缺失热点
perf record -a -g -e cache-misses sleep 10

# 查看可用事件列表
perf list

perf stat 适合对单次运行做整体计数,perf record 适合定位具体热点。

六、磁盘 IO 基准:fio

fio 用于生成可控的磁盘 IO 负载,测试磁盘的 IOPS 和吞吐。

5.1 顺序写

fio --name=seq_write \
    --filename=/data/fio/testfile \
    --size=100G --rw=write --bs=1m \
    --direct=1 --ioengine=libaio \
    --numjobs=4 --runtime=600 --group_reporting

5.2 顺序读

fio --name=seq_read \
    --filename=/data/fio/testfile \
    --rw=read --bs=1m \
    --direct=1 --ioengine=libaio \
    --numjobs=4 --runtime=600 --group_reporting

参数含义:

参数 含义
--rw IO 模式:read、write、randread、randwrite 等
--bs 单次 IO 块大小
--direct=1 绕过页缓存直接读写磁盘
--ioengine IO 引擎,如 libaio、sync
--numjobs 并发作业数
--runtime 测试时长(秒)
--group_reporting 汇总所有作业的结果

测试大文件时注意磁盘剩余空间,--size=100G 会实际分配文件。

七、网络吞吐:iperf3

iperf3 用于测量两台主机之间的 TCP/UDP 带宽。

6.1 服务端

sudo apt install iperf3 -y
iperf3 -s

默认监听 5201 端口,也可以指定端口:

iperf3 -s -p 5202

6.2 客户端

iperf3 -c x.x.x.x -p 5201 -t 60 -i 5
  • -c:服务端 IP。
  • -p:服务端端口,默认 5201。
  • -t:测试时长(秒)。
  • -i:结果打印间隔(秒)。

测试结果中重点看 Bitrate 和 Retr(重传次数)。带宽远低于网卡上限或重传较多时,需要继续检查瓶颈是 CPU、驱动还是物理链路。

八、工具选择总结

场景 工具 关注点
快速检查整机健康 60 秒清单 负载、CPU、内存、磁盘、网络
程序耗时与峰值内存 /usr/bin/time -v User/System time、RSS、页错误
定位函数热点(开发期) gprof / gprofng 函数耗时占比、调用次数
定位函数热点(免编译) perf / 火焰图 调用栈采样、热点函数
磁盘性能测试 fio IOPS、吞吐、延迟
网络带宽测试 iperf3 Bitrate、重传
制造压力负载 stress-ng CPU、内存、磁盘、网络压测

九、总结

  • 先用 60 秒检查清单快速定位瓶颈方向,再按进程、磁盘、网络逐层深入。
  • 分析内存时注意区分页缓存和可用内存,swap 持续增长才是内存压力信号。
  • /usr/bin/time -v 可以快速获取程序的耗时、RSS 和页错误。
  • gprof 适合开发阶段的函数热点分析,生产环境优先考虑采样型工具。
  • perf 无需重新编译即可采样调用栈,配合火焰图可以直观定位 CPU 热点。
  • fio 和 iperf3 用于可控的磁盘、网络基准测试,结果要结合硬件规格解读。
  • 压测和监控配合使用:先用 stress-ng 制造负载,再用本文工具观察系统表现。

相关脚本

整理自 cyy-tag/bash-example 仓库: