场景手册

从症状出发:先确认什么、敲什么、看哪个数、下一步去哪,最后指到书里的节号(译本节号相同)。场景和步骤是本站整理的,依据是书里的方法和工具;命令的细节和更多选项见各章导读的「工具速查」和本站的工具卡。

动手之前先问清楚问题。书里的「问题陈述」(§2.5.5)是一串很朴素的问题:你凭什么认为有性能问题?以前快过吗?最近改了什么?能用延迟或运行时间说清楚吗?影响到别人了吗?环境是什么?很多问题在回答这几句时就有了方向。实在不知道从哪下手,书里 §1.10.1 有一张「头 60 秒」的首查清单,十条传统命令,每条写了看什么、在哪一节,第一分钟照着跑一遍;§2.5.9 的 USE 方法是更系统的版本。

CPU

CPU 很忙,不知道忙在哪

症状:top 里 CPU 接近满,或者某个服务进程的 %CPU 很高,延迟跟着上去。

  1. 按核看:mpstat -P ALL 1。全都满是整体不够;只有一两个核满是单线程或中断集中(看 %soft、%irq)。
  2. 找到进程和线程:pidstat 1,再 pidstat -t -p PID 1,或者 top -H -p PID。分开看 %usr 和 %system:内核态多,往系统调用和中断上查。
  3. 采样剖析:perf record -F 99 -g -p PID -- sleep 30,perf report --stdio,或者画成火焰图。先看最宽的塔,再看顶上的平台。看到一串十六进制地址是缺符号,栈断成一两层是缺帧指针。
  4. 看周期: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、等网络、等别的服务。

  1. 先看线程的状态:top -H -p PID 的 S 列,或 ps -L -o tid,stat,wchan:20,comm -p PID。大量 D 状态多半在等存储;S 状态要看在睡什么。
  2. 看自愿上下文切换:pidstat -w -t -p PID 1 的 cswch/s。高说明线程频繁主动让出 CPU(阻塞)。
  3. 系统调用:strace -c -f -p PID 统计十几秒(strace 开销很大,生产上慎用),看时间花在哪些调用上;perf trace -s -p PID 开销低一些(要权限)。
  4. off-CPU 分析:offcputime-bpfcc -p PID 10(要 root)给出线程阻塞时的栈和时长,能直接看到是哪把锁、哪个读调用。
  5. 再往外:下游服务、DNS、磁盘、网络。把一次请求的延迟按层拆开(延迟分析)。

翻书:§5.4.2 off-CPU 分析,§5.4.3 系统调用分析,§5.4.5 线程状态分析,§5.4.6 锁分析,§2.5.13 延迟分析。第 5 章导读 →

负载平均很高

症状:uptime 的负载远大于核数,告警响了。

  1. 先看趋势:三个数一比,1 分钟的远低于 15 分钟的,高峰可能已经过去。
  2. 分清是哪一种负载:vmstat 1 的 r(在跑和排队的)和 b(不可中断睡眠,多半在等 IO)。Linux 的负载两者都算。
  3. 按资源看压力:cat /proc/pressure/cpu /proc/pressure/io /proc/pressure/memory,哪一个的 avg10 高就往哪查。
  4. 找 D 状态的线程:ps -eLo stat,comm | awk '$1 ~ /^D/' | sort | uniq -c,再按 IO 的场景往下走。

翻书:§6.6.1 负载平均和 PSI,§6.6.2 vmstat。书里建议在负载平均上花的时间别超过五分钟。第 6 章导读 →

线程在排队等 CPU

症状:CPU 利用率高或接近满,延迟抖动;或者容器里 CPU 没满却慢。

  1. vmstat 1 的 r 长期大于核数;pidstat -p PID 1 的 %wait 明显不为零。
  2. 量排队时间:runqlat-bpfcc 10 1(要 root),看直方图里毫秒级的桶。开销顾虑大时用 runqlen-bpfcc 10 1。
  3. 容器里先排除限额:cgroup v2 的 cpu.stat 里 nr_throttled、throttled_usec 在涨,就是被限额节流了(见「容器、虚拟机」)。
  4. 缓解:给后台任务降优先级(nice、renice)、限它的 CPU、错开时间,或者加核。这时加线程通常只会让队更长。

翻书:§6.3.10 饱和度,§6.6.16 runqlat,§6.6.17 runqlen,§6.5.8 优先级调优,§6.9.8 资源控制。第 6 章导读 →

内存

内存一直涨

症状:服务的 RSS 一天比一天大,或者系统的可用内存越来越少。

  1. 先分清是谁在涨:free -m 的 available 才是还能用的;buff/cache 大是文件缓存,内存紧时会被回收,通常不是问题。
  2. 看进程:pidstat -r -p PID 60 长时间记录 RSS;ps -o pid,rss,vsz,comm -p PID。
  3. 看涨在哪块:pmap -x PID 或 cat /proc/PID/smaps_rollup,堆(匿名内存)在涨还是映射的文件在涨。
  4. 内核那边:slabtop 或 /proc/meminfo 的 Slab、SUnreclaim,排除内核对象泄漏。
  5. 先问一句:它本来就该涨吗?书里提醒,正常的内存增长(比如缓存在填充)常被误认成泄漏。要定位,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 杀掉,或者开始换页

症状:进程莫名消失;系统变得极慢、磁盘灯狂闪。

  1. 确认是不是 OOM:dmesg -T | grep -i -E 'out of memory|killed process',记录里有被杀进程当时的内存。
  2. 看换页:vmstat 1 的 si、so 持续不为零就是在换页;/proc/pressure/memory 的 full 一行高,说明任务整体卡在内存上。
  3. 找大户:ps -eo pid,rss,comm --sort=-rss | head;容器里看 cgroup 的 memory.current、memory.max、memory.events。
  4. 决定怎么办:限住不重要的进程、调整 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 高;板子上写日志时整个程序卡一下。

  1. 从应用这一层量延迟:strace -T -e trace=read,write,fsync -p PID 看单次调用的耗时(开销大,短时间用);ext4slower-bpfcc 10(要 root)列出慢于 10 毫秒的文件操作。
  2. 看缓存命中:free -m 的 buff/cache 有多大;cachestat-bpfcc 1(要 root)给出页缓存命中率。命中率低多半是在读盘。
  3. 看同步写:频繁的 fsync、O_SYNC 会让每次写都等到盘上,在 eMMC、SD 卡上尤其慢。
  4. 再往下看磁盘本身(见下一个场景)。注意 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 延迟比平时大。

  1. iostat -xz 1:r/s、w/s(IOPS)、rkB/s、wkB/s(吞吐)、r_await、w_await(平均延迟,含排队)、aqu-sz(平均队列长度)。
  2. %util 在 SSD 和阵列上会误导:它们能并行处理请求,100% 不等于饱和。延迟和队列长度更可信。
  3. 谁在读写:pidstat -d 1;biotop-bpfcc(要 root)。
  4. 延迟分布: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 章导读 →

网络

网络请求延迟高或抖动

症状:客户端看到的延迟比服务端自己记的处理时间长,或者时快时慢。

  1. 先拆开:连接建立、首字节、传输各花多少。服务端处理时间短而客户端延迟长,问题在路上或在队列里。
  2. 看连接级的指标:ss -tin 给出每个连接的 rtt、cwnd、重传次数。
  3. 看重传和错误:nstat -z | grep -i -E 'retrans|drop'(隔几秒再跑一次,看增量);sar -n EDEV,ETCP 1。tcpretrans-bpfcc(要 root)逐条列出重传。
  4. 看接收端的 CPU:网卡软中断集中在一个核(mpstat -P ALL 1 的 %soft)会让包排队。
  5. 基线:ping 的 RTT 抖不抖,和平时比。

翻书:§10.5.4 延迟分析,§10.3.5 延迟,§10.6.1 ss,§10.6.4 nstat,§10.6.11 tcpretrans。第 10 章导读 →

新连接被拒、超时、偶尔连不上

症状:高峰时客户端偶发连接超时或要等一秒以上才连上,服务端日志却很平静。

  1. 看监听队列:ss -lnt,监听套接字的 Recv-Q 是当前排队的连接数,Send-Q 是队列上限。Recv-Q 贴着上限就是 accept 不过来。
  2. 看丢弃计数:nstat -z | grep -i listen,隔几秒再跑一次,TcpExtListenOverflows、TcpExtListenDrops 在涨就是队列满了在丢。客户端的 SYN 被丢后要等重传(Linux 上首次重传约在 1 秒后),所以常表现为「慢一秒才连上」。
  3. 修:让 accept 循环跑得更快(别在 accept 线程里干重活),调大 listen() 的 backlog 和 net.core.somaxconn(实际上限取两者较小的)。

翻书:§10.3.7 连接队列,§10.6.4 nstat,§10.8.1 全系统调优。第 10 章导读 →

吞吐上不去、重传多

症状:传大文件或批量同步时速度远低于带宽。

  1. 和链路比:sar -n DEV 1 看实际收发速率,ethtool 网卡名 看协商的速率和双工。
  2. 看重传:nstat -z(不带 -a 时显示上次运行以来的增量)里 TcpRetransSegs 相对 TcpOutSegs 的比例;ss -tin 看拥塞窗口。
  3. 看缓冲区:长距离、高带宽的连接需要足够大的套接字缓冲区(带宽时延积),相关参数是 net.ipv4.tcp_rmem、tcp_wmem。
  4. 单独测网络:iperf3 在两端测一下,排除应用本身。

翻书:§10.5.7 TCP 分析,§10.3.6 缓冲,§10.3.9 拥塞避免,§10.7.4 iperf,§10.8.1 全系统调优。第 10 章导读 →

不分资源的

平均值正常,p99 很高

症状:监控上平均延迟很好看,用户却抱怨偶尔很慢。

  1. 别看平均,看分布:把延迟记成直方图或百分位(p50、p99、p99.9)。多峰分布里平均值落在两个峰之间,谁也代表不了。
  2. 找出慢的那一类:慢请求有没有共同点(某个接口、某个分片、某段时间、缓存未命中)。
  3. 看周期性的干扰:定时任务、日志轮转、内存回收、后台备份。秒级的平均会把它们抹平;FlameScope 一类按亚秒看的工具能看出来。
  4. 资源层面的分布:runqlat-bpfcc、biolatency-bpfcc 给出的都是直方图(要 root),对照慢请求的时间。

翻书:§2.8.3 百分位,§2.8.5 多峰分布,§2.8.6 离群值,§2.10.3 热图,§6.7.4 FlameScope。第 2 章导读 →

升级或改配置后变慢

症状:换了版本、内核、编译器或配置,性能掉了。

  1. 先确认可复现:同样的负载、同样的数据,新旧各跑几次,看差距是否超过波动。
  2. 用科学方法:一次只改一个变量,提出假设、做实验验证(§2.5.6)。
  3. 新旧对比几样东西:perf stat 的周期、指令、IPC;两张火焰图;系统调用计数(strace -c)。差在哪一项,就往哪一章走。
  4. 查静态配置:内核参数、CPU 频率调节器、cgroup 限额、编译选项(§2.5.17 的静态性能调优)。

翻书:§1.11.2 一次软件变更的案例,§2.5.6 科学方法,§2.5.17 静态性能调优,§2.8.1 量化性能收益。第 1 章导读 →

容器或虚拟机里性能不对

症状:容器里 top 显示 CPU 很闲,服务却慢;或者虚拟机的性能时好时坏。

  1. CPU 限额:找到容器的 cgroup(cat /proc/PID/cgroup),读 /sys/fs/cgroup/…/cpu.max(配额)和 cpu.stat。nr_throttled、throttled_usec 在涨就是被节流了:利用率要按限额算,不是按物理核。
  2. 内存限额:同一目录下 memory.current、memory.max、memory.events(里面有 oom_kill 计数)。
  3. 容器里的工具看到的常常是宿主的全局数据(比如 /proc/meminfo、/proc/stat),别直接拿来当容器的。
  4. 虚拟机:mpstat 的 %steal、vmstat 的 st 是宿主把这台机器的 CPU 时间给了别人;硬件计数器在不少云主机上拿不到或不全。

翻书:§11.3.3 容器的资源控制,§11.3.4 容器的可观测性,§11.2.4 硬件虚拟化的可观测性,§6.5.2 限额下的 USE 方法。第 11 章导读 →

嵌入式板子上没有趁手的工具

症状:BSP 裁剪过的系统只有 busybox,没有 sysstat、perf、bpftrace。

  1. /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 显示的数就是从这些文件里差分出来的,自己隔一秒读两次相减就行。
  2. 内核开了 PSI,就有 /proc/pressure/*;开了 ftrace,就能通过 tracefs 用 echo 和 cat 跟踪函数和跟踪点。
  3. 趁平时把工具准备好:书里有一节专门讲「危机工具」,出事时再装往往来不及。perf 可以从内核源码交叉编译,sysstat 也能交叉编译,提前放进固件。
  4. 先查内核配置: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 章导读 →

要做性能对比或压测

症状:要比较两个版本、两台机器、两个配置,或者给出一个「能扛多少」的数。

  1. 主动基准测试:测的时候开着 mpstat、pidstat、iostat,确认瓶颈真的在你以为的地方。书里有一句狠话:花在研究一个基准测试结果上的时间不到一周,它多半是错的。
  2. 先做健全性检查:结果符合物理常识吗?比如超过网卡带宽、超过磁盘的标称 IOPS 的数,多半说明结果有问题,常见原因是测到了缓存(书里的例子是测到了客户端缓存)。
  3. 逐步加负载:并发从小往大加,画出吞吐和延迟随负载变化的曲线,找到拐点,而不是只报一个最大值。
  4. 重复几次,报告波动(标准差或百分位),不只报平均值。

翻书:§12.3.2 主动基准测试,§12.3.8 健全性检查,§12.3.7 逐步加负载,§12.3.9 统计分析,§2.5.19 微基准测试。第 12 章导读 →