场景手册
从症状出发:先确认什么、敲什么、看哪个数、下一步去哪,最后指到书里的节号(译本节号相同)。场景和步骤是本站整理的,依据是书里的方法和工具;命令的细节和更多选项见各章导读的「工具速查」和本站的工具卡。
动手之前先问清楚问题。书里的「问题陈述」(§2.5.5)是一串很朴素的问题:你凭什么认为有性能问题?以前快过吗?最近改了什么?能用延迟或运行时间说清楚吗?影响到别人了吗?环境是什么?很多问题在回答这几句时就有了方向。实在不知道从哪下手,书里 §1.10.1 有一张「头 60 秒」的首查清单,十条传统命令,每条写了看什么、在哪一节,第一分钟照着跑一遍;§2.5.9 的 USE 方法是更系统的版本。
CPU
CPU 很忙,不知道忙在哪
症状:top 里 CPU 接近满,或者某个服务进程的 %CPU 很高,延迟跟着上去。
- 按核看:
mpstat -P ALL 1。全都满是整体不够;只有一两个核满是单线程或中断集中(看%soft、%irq)。 - 找到进程和线程:
pidstat 1,再pidstat -t -p PID 1,或者top -H -p PID。分开看%usr和%system:内核态多,往系统调用和中断上查。 - 采样剖析:
perf record -F 99 -g -p PID -- sleep 30,perf report --stdio,或者画成火焰图。先看最宽的塔,再看顶上的平台。看到一串十六进制地址是缺符号,栈断成一两层是缺帧指针。 - 看周期:
perf stat -p PID -- sleep 10的 IPC。低就往内存访问上查,高就设法少执行指令。
翻书:§6.5.4 剖析,§6.6.13 perf,§6.7.3 火焰图,§5.4.1 应用的 CPU 剖析,§5.6 缺符号和缺栈。第 6 章导读 →
板子上:没有 perf 时,隔一秒读两次 /proc/PID/task/*/stat 里的 utime、stime(第 14、15 个字段;第 2 个字段是括号里的线程名,名字里可能带空格,从右括号之后往后数),看哪个线程在涨;perf 可以交叉编译。
慢,但 CPU 不忙
症状:请求慢,CPU 却有大把空闲。时间不在 CPU 上:线程在等锁、等 IO、等网络、等别的服务。
- 先看线程的状态:
top -H -p PID的 S 列,或ps -L -o tid,stat,wchan:20,comm -p PID。大量 D 状态多半在等存储;S 状态要看在睡什么。 - 看自愿上下文切换:
pidstat -w -t -p PID 1的cswch/s。高说明线程频繁主动让出 CPU(阻塞)。 - 系统调用:
strace -c -f -p PID统计十几秒(strace 开销很大,生产上慎用),看时间花在哪些调用上;perf trace -s -p PID开销低一些(要权限)。 - off-CPU 分析:
offcputime-bpfcc -p PID 10(要 root)给出线程阻塞时的栈和时长,能直接看到是哪把锁、哪个读调用。 - 再往外:下游服务、DNS、磁盘、网络。把一次请求的延迟按层拆开(延迟分析)。
翻书:§5.4.2 off-CPU 分析,§5.4.3 系统调用分析,§5.4.5 线程状态分析,§5.4.6 锁分析,§2.5.13 延迟分析。第 5 章导读 →
负载平均很高
症状:uptime 的负载远大于核数,告警响了。
- 先看趋势:三个数一比,1 分钟的远低于 15 分钟的,高峰可能已经过去。
- 分清是哪一种负载:
vmstat 1的r(在跑和排队的)和b(不可中断睡眠,多半在等 IO)。Linux 的负载两者都算。 - 按资源看压力:
cat /proc/pressure/cpu /proc/pressure/io /proc/pressure/memory,哪一个的avg10高就往哪查。 - 找 D 状态的线程:
ps -eLo stat,comm | awk '$1 ~ /^D/' | sort | uniq -c,再按 IO 的场景往下走。
翻书:§6.6.1 负载平均和 PSI,§6.6.2 vmstat。书里建议在负载平均上花的时间别超过五分钟。第 6 章导读 →
线程在排队等 CPU
症状:CPU 利用率高或接近满,延迟抖动;或者容器里 CPU 没满却慢。
vmstat 1的r长期大于核数;pidstat -p PID 1的%wait明显不为零。- 量排队时间:
runqlat-bpfcc 10 1(要 root),看直方图里毫秒级的桶。开销顾虑大时用runqlen-bpfcc 10 1。 - 容器里先排除限额:cgroup v2 的
cpu.stat里nr_throttled、throttled_usec在涨,就是被限额节流了(见「容器、虚拟机」)。 - 缓解:给后台任务降优先级(
nice、renice)、限它的 CPU、错开时间,或者加核。这时加线程通常只会让队更长。
翻书:§6.3.10 饱和度,§6.6.16 runqlat,§6.6.17 runqlen,§6.5.8 优先级调优,§6.9.8 资源控制。第 6 章导读 →
内存
内存一直涨
症状:服务的 RSS 一天比一天大,或者系统的可用内存越来越少。
- 先分清是谁在涨:
free -m的available才是还能用的;buff/cache大是文件缓存,内存紧时会被回收,通常不是问题。 - 看进程:
pidstat -r -p PID 60长时间记录 RSS;ps -o pid,rss,vsz,comm -p PID。 - 看涨在哪块:
pmap -x PID或cat /proc/PID/smaps_rollup,堆(匿名内存)在涨还是映射的文件在涨。 - 内核那边:
slabtop或/proc/meminfo的 Slab、SUnreclaim,排除内核对象泄漏。 - 先问一句:它本来就该涨吗?书里提醒,正常的内存增长(比如缓存在填充)常被误认成泄漏。要定位,BCC 的
memleak-bpfcc -p PID(要 root)记下一段时间里分配了没释放的内存和它的调用路径,但它分不清泄漏和正常增长,要你看代码判断。C/C++ 程序在测试环境里也可以用 valgrind、AddressSanitizer(书外)。
翻书:§7.4.6 泄漏检测,§7.4.3 使用情况的特征,§7.5.9 pmap,§7.5.5 slabtop,§7.2.8 分配器。第 7 章导读 →
板子上:/proc/meminfo 和 /proc/PID/status 的 VmRSS、VmData 随时可读,写个脚本每分钟记一次就是最便宜的监控。
被 OOM 杀掉,或者开始换页
症状:进程莫名消失;系统变得极慢、磁盘灯狂闪。
- 确认是不是 OOM:
dmesg -T | grep -i -E 'out of memory|killed process',记录里有被杀进程当时的内存。 - 看换页:
vmstat 1的si、so持续不为零就是在换页;/proc/pressure/memory的full一行高,说明任务整体卡在内存上。 - 找大户:
ps -eo pid,rss,comm --sort=-rss | head;容器里看 cgroup 的memory.current、memory.max、memory.events。 - 决定怎么办:限住不重要的进程、调整 OOM 优先级(
/proc/PID/oom_score_adj)、减少内存使用,或者加内存。
翻书:§7.2.7 内存的利用率和饱和度,§7.2.4 过量分配,§7.5.1 vmstat,§7.5.2 PSI,§7.6.5 资源控制。第 7 章导读 →
文件和磁盘
读写文件慢、iowait 高
症状:应用的读写调用慢;top 里 wa 高;板子上写日志时整个程序卡一下。
- 从应用这一层量延迟:
strace -T -e trace=read,write,fsync -p PID看单次调用的耗时(开销大,短时间用);ext4slower-bpfcc 10(要 root)列出慢于 10 毫秒的文件操作。 - 看缓存命中:
free -m的 buff/cache 有多大;cachestat-bpfcc 1(要 root)给出页缓存命中率。命中率低多半是在读盘。 - 看同步写:频繁的
fsync、O_SYNC会让每次写都等到盘上,在 eMMC、SD 卡上尤其慢。 - 再往下看磁盘本身(见下一个场景)。注意
wa只说明「CPU 闲着时有线程在等 IO」,不是 IO 的延迟。
翻书:§8.5.2 延迟分析,§8.3.7 同步写,§8.3.2 缓存,§8.6.12 cachestat,§8.6.14 ext4slower,§9.3.11 I/O Wait。第 8 章导读 →
磁盘忙、延迟高
症状:iostat 里某块盘的利用率很高,或者 IO 延迟比平时大。
iostat -xz 1:r/s、w/s(IOPS)、rkB/s、wkB/s(吞吐)、r_await、w_await(平均延迟,含排队)、aqu-sz(平均队列长度)。%util在 SSD 和阵列上会误导:它们能并行处理请求,100% 不等于饱和。延迟和队列长度更可信。- 谁在读写:
pidstat -d 1;biotop-bpfcc(要 root)。 - 延迟分布:
biolatency-bpfcc 10 1(要 root),看是不是多峰;biosnoop-bpfcc逐条看慢的那几次。
翻书:§9.6.1 iostat,§9.3.9 利用率,§9.3.10 饱和度,§9.5.2 USE 方法,§9.6.6 biolatency,§9.6.7 biosnoop。第 9 章导读 →
网络
网络请求延迟高或抖动
症状:客户端看到的延迟比服务端自己记的处理时间长,或者时快时慢。
- 先拆开:连接建立、首字节、传输各花多少。服务端处理时间短而客户端延迟长,问题在路上或在队列里。
- 看连接级的指标:
ss -tin给出每个连接的 rtt、cwnd、重传次数。 - 看重传和错误:
nstat -z | grep -i -E 'retrans|drop'(隔几秒再跑一次,看增量);sar -n EDEV,ETCP 1。tcpretrans-bpfcc(要 root)逐条列出重传。 - 看接收端的 CPU:网卡软中断集中在一个核(
mpstat -P ALL 1的%soft)会让包排队。 - 基线:
ping的 RTT 抖不抖,和平时比。
翻书:§10.5.4 延迟分析,§10.3.5 延迟,§10.6.1 ss,§10.6.4 nstat,§10.6.11 tcpretrans。第 10 章导读 →
新连接被拒、超时、偶尔连不上
症状:高峰时客户端偶发连接超时或要等一秒以上才连上,服务端日志却很平静。
- 看监听队列:
ss -lnt,监听套接字的Recv-Q是当前排队的连接数,Send-Q是队列上限。Recv-Q贴着上限就是 accept 不过来。 - 看丢弃计数:
nstat -z | grep -i listen,隔几秒再跑一次,TcpExtListenOverflows、TcpExtListenDrops在涨就是队列满了在丢。客户端的 SYN 被丢后要等重传(Linux 上首次重传约在 1 秒后),所以常表现为「慢一秒才连上」。 - 修:让 accept 循环跑得更快(别在 accept 线程里干重活),调大
listen()的 backlog 和net.core.somaxconn(实际上限取两者较小的)。
翻书:§10.3.7 连接队列,§10.6.4 nstat,§10.8.1 全系统调优。第 10 章导读 →
吞吐上不去、重传多
症状:传大文件或批量同步时速度远低于带宽。
- 和链路比:
sar -n DEV 1看实际收发速率,ethtool 网卡名看协商的速率和双工。 - 看重传:
nstat -z(不带-a时显示上次运行以来的增量)里TcpRetransSegs相对TcpOutSegs的比例;ss -tin看拥塞窗口。 - 看缓冲区:长距离、高带宽的连接需要足够大的套接字缓冲区(带宽时延积),相关参数是
net.ipv4.tcp_rmem、tcp_wmem。 - 单独测网络:
iperf3在两端测一下,排除应用本身。
翻书:§10.5.7 TCP 分析,§10.3.6 缓冲,§10.3.9 拥塞避免,§10.7.4 iperf,§10.8.1 全系统调优。第 10 章导读 →
不分资源的
平均值正常,p99 很高
症状:监控上平均延迟很好看,用户却抱怨偶尔很慢。
- 别看平均,看分布:把延迟记成直方图或百分位(p50、p99、p99.9)。多峰分布里平均值落在两个峰之间,谁也代表不了。
- 找出慢的那一类:慢请求有没有共同点(某个接口、某个分片、某段时间、缓存未命中)。
- 看周期性的干扰:定时任务、日志轮转、内存回收、后台备份。秒级的平均会把它们抹平;FlameScope 一类按亚秒看的工具能看出来。
- 资源层面的分布:
runqlat-bpfcc、biolatency-bpfcc给出的都是直方图(要 root),对照慢请求的时间。
翻书:§2.8.3 百分位,§2.8.5 多峰分布,§2.8.6 离群值,§2.10.3 热图,§6.7.4 FlameScope。第 2 章导读 →
升级或改配置后变慢
症状:换了版本、内核、编译器或配置,性能掉了。
- 先确认可复现:同样的负载、同样的数据,新旧各跑几次,看差距是否超过波动。
- 用科学方法:一次只改一个变量,提出假设、做实验验证(§2.5.6)。
- 新旧对比几样东西:
perf stat的周期、指令、IPC;两张火焰图;系统调用计数(strace -c)。差在哪一项,就往哪一章走。 - 查静态配置:内核参数、CPU 频率调节器、cgroup 限额、编译选项(§2.5.17 的静态性能调优)。
翻书:§1.11.2 一次软件变更的案例,§2.5.6 科学方法,§2.5.17 静态性能调优,§2.8.1 量化性能收益。第 1 章导读 →
容器或虚拟机里性能不对
症状:容器里 top 显示 CPU 很闲,服务却慢;或者虚拟机的性能时好时坏。
- CPU 限额:找到容器的 cgroup(
cat /proc/PID/cgroup),读/sys/fs/cgroup/…/cpu.max(配额)和cpu.stat。nr_throttled、throttled_usec在涨就是被节流了:利用率要按限额算,不是按物理核。 - 内存限额:同一目录下
memory.current、memory.max、memory.events(里面有oom_kill计数)。 - 容器里的工具看到的常常是宿主的全局数据(比如
/proc/meminfo、/proc/stat),别直接拿来当容器的。 - 虚拟机:
mpstat的%steal、vmstat的st是宿主把这台机器的 CPU 时间给了别人;硬件计数器在不少云主机上拿不到或不全。
翻书:§11.3.3 容器的资源控制,§11.3.4 容器的可观测性,§11.2.4 硬件虚拟化的可观测性,§6.5.2 限额下的 USE 方法。第 11 章导读 →
嵌入式板子上没有趁手的工具
症状:BSP 裁剪过的系统只有 busybox,没有 sysstat、perf、bpftrace。
/proc和/sys几乎总在:/proc/stat(CPU 各项时间)、/proc/loadavg、/proc/meminfo、/proc/PID/status和/proc/PID/task/*/stat、/proc/interrupts、/proc/softirqs、/proc/diskstats、/proc/net/snmp和/proc/net/netstat。mpstat、iostat、nstat 显示的数就是从这些文件里差分出来的,自己隔一秒读两次相减就行。- 内核开了 PSI,就有
/proc/pressure/*;开了 ftrace,就能通过 tracefs 用echo和cat跟踪函数和跟踪点。 - 趁平时把工具准备好:书里有一节专门讲「危机工具」,出事时再装往往来不及。perf 可以从内核源码交叉编译,sysstat 也能交叉编译,提前放进固件。
- 先查内核配置:
zcat /proc/config.gz | grep -E 'PSI|FTRACE|BPF_SYSCALL|SCHED_INFO'(要内核开了CONFIG_IKCONFIG_PROC)。
翻书:§4.1.2 危机工具,§4.3.1 /proc,§4.3.2 /sys,§14.2 tracefs,§14.13 perf-tools。第 4 章导读 →
要做性能对比或压测
症状:要比较两个版本、两台机器、两个配置,或者给出一个「能扛多少」的数。
- 主动基准测试:测的时候开着
mpstat、pidstat、iostat,确认瓶颈真的在你以为的地方。书里有一句狠话:花在研究一个基准测试结果上的时间不到一周,它多半是错的。 - 先做健全性检查:结果符合物理常识吗?比如超过网卡带宽、超过磁盘的标称 IOPS 的数,多半说明结果有问题,常见原因是测到了缓存(书里的例子是测到了客户端缓存)。
- 逐步加负载:并发从小往大加,画出吞吐和延迟随负载变化的曲线,找到拐点,而不是只报一个最大值。
- 重复几次,报告波动(标准差或百分位),不只报平均值。
翻书:§12.3.2 主动基准测试,§12.3.8 健全性检查,§12.3.7 逐步加负载,§12.3.9 统计分析,§2.5.19 微基准测试。第 12 章导读 →