2性能之巅:系统、企业与云可观测性(第2版)

性能方法论

USE 方法与工作负载刻画

USE方法工作负载刻画科学方法反模式

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第2章 性能方法论

USE 方法与工作负载刻画

章节导读

如果说第 1 章回答了"性能是什么",那么第 2 章回答的是"性能分析该怎么做"。方法论是 Gregg 在这本书中最具原创性的贡献——大多数性能书籍罗列工具,而这本书教你一套可迁移的思维框架。工具会过时(strace 让位于 bpftrace,sar 让位于 Prometheus),但方法论历久弥新。

先看一个反例。某团队的服务变慢,工程师 A 登录机器跑了 top,看到 CPU 不高,说"不是 CPU 问题,应该是数据库慢";DBA 检查数据库,发现查询都走了索引,说"数据库没问题,可能是网络";网络组 ping 了一下,延迟正常,说"网络没问题"。三天过去,问题依旧,每个人都在自己熟悉的路灯下找钥匙——这就是著名的"街头灯柱效应"(streetlight effect):醉汉在路灯下找钥匙,不是因为在路灯下丢的,而是因为那里有光。工程师倾向于只用自己熟悉的工具、只检查自己负责的组件,而性能问题恰恰喜欢藏在没人看的角落。

Gregg 提出的解药是 USE 方法:对每一种资源,检查它的利用率(Utilization)、饱和度(Saturation)、错误(Errors)。资源包括所有功能部件:CPU、内存、磁盘、网络,也包括容易被忽略的软件资源——互斥锁、线程池、连接池、文件描述符表、甚至内核的 SNAT 端口空间。USE 方法的威力在于它是穷举式的清单:照着做,你不会漏掉任何资源;它也是快速排除式的:每种资源三个指标,几分钟就能扫完全系统。Gregg 在 Netflix 工作时用 USE 方法把许多"查了三天没头绪"的问题在 30 分钟内定位。书中给出了完整的 Linux USE 清单:对 CPU,利用率看 mpstat 的 %usr/%sys,饱和度看 vmstat 的 r 列与 /proc/pressure/cpu,错误看 perf 的 machine-checks;对内存,利用率看 free,饱和度看 si/so 与 PSI memory,错误看 dmesg 的 EDAC/OOM;对存储,利用率看 iostat 的 %util,饱和度看 aqu-sz 与 PSI io,错误看 smartctl 与 dmesg;对网络,利用率看 sar -n DEV 对比带宽,饱和度看 netstat -s 的溢出计数(listen queue overflow),错误看 ifconfig/ip -s link 的 errors/dropped。

USE 方法之外,工作负载刻画(Workload Characterization)同样关键。性能 = 系统能力 / 施加的负载,只看系统不看负载是半盲的。刻画负载要回答:请求以什么速率到达(QPS)?每个请求触发多少工作(逻辑 I/O 数、查询复杂度)?负载的分布特征是什么(热点 key、大小分布、读写比例)?负载从哪来(客户端画像)?一个经典案例:数据库 CPU 100%,团队忙着优化 SQL,但工作负载刻画显示 QPS 比上周翻了 4 倍——原来是某个客户端的重试风暴,根因在客户端的退避策略,优化 SQL 是治标。

科学方法是贯穿一切分析活动的元方法:观察现象 → 提出假设 → 设计实验预测结果 → 验证或证伪 → 迭代。性能排障中最常见的堕落形式是"随机变更法"(random change theory):不做假设,重启试试、扩容试试、改个参数试试,碰巧好了就收工——问题必然复发,因为你从未理解根因。科学方法要求每个操作都有明确目的:这个命令是为了验证什么假设?预期看到什么?看到别的又说明什么?

本章还将介绍 Gregg 的其他方法论:问题画像法(先问十个基本问题:什么变慢了?从什么时候开始?最近有什么变更?)、钻取分析法(从系统级指标逐层钻取到进程、线程、函数)、事件追踪法、以及基准测试的科学设计(第 11 章详述)。这些方法论共同构成一个决策树:接到性能问题报告,先做问题画像缩小范围,用 60 秒清单和 USE 方法扫描资源,用工作负载刻画确认负载侧变化,形成假设后用剖析/追踪工具验证。

方法论的最后一条军规是:警惕反模式。除了街头灯柱效应,还有"怪罪他人"(在没有证据时把问题推给别的团队)、"无基准的优化"(不知道优化前是多少,就无法证明优化有效)、"把相关当因果"(CPU 高的时候延迟也高,不代表 CPU 高导致延迟高——可能两者都是流量上涨的结果)、"在错误的层次解决问题"(应用层重试风暴导致的数据库过载,去调数据库参数)。识别并避免这些反模式,比掌握任何单个工具都重要。

核心概念

  • USE方法
  • 工作负载刻画
  • 科学方法
  • 反模式

要点精析

USE 方法的三要素需要精确定义。利用率(Utilization)是资源在一段时间内忙碌的时间比例,如 CPU 使用率 90%、磁盘 %util 80%;注意对并行资源(多核 CPU、多队列 NVMe)利用率口径要小心——单核 100% 而整机 12% 时,整机利用率掩盖了单核瓶颈。饱和度(Saturation)是资源无法立即处理请求而排队的程度,度量方式因资源而异:CPU 是运行队列长度(vmstat 的 r 列)或调度延迟,内存是 swap 活动或回收扫描速率,磁盘是 I/O 队列深度(aqu-sz)或 await 升高,网络是发送队列积压或丢包重传,软件资源(线程池)是等待队列长度。错误(Errors)包括硬件错误(ECC、CRC、坏道)与软件错误计数(溢出、超时、拒绝)。USE 的精髓是"穷举 + 快速排除":对每种资源依次问三个问题,任何一个指标异常都是线索,全部正常则果断排除该资源,把精力集中到别处。

USE 方法必须覆盖软件资源,这是最容易被忽略的部分。硬件资源(CPU/内存/磁盘/网络)有成熟工具,但软件资源同样会饱和:互斥锁的饱和度是锁等待时间与竞争线程数(perf lock、bpftrace 追踪 pthread_mutex);线程池的饱和度是队列中等待的任务数;数据库连接池的饱和度是等待连接的请求数(应用指标);文件描述符的饱和度是 fd 使用量逼近 ulimit(lsof | wc -l 对比 ulimit -n);TCP 监听队列(backlog)的饱和度是 SYN queue overflow 与 accept queue overflow 计数(netstat -s | grep -i listen);conntrack 表的饱和度是 nf_conntrack_count 逼近 max(dmesg 会有 table full 报错);甚至内核内存(slab)也会饱和。Gregg 的建议是为你的系统画一张"资源清单图",把硬件与软件资源全部列出,逐一标注 USE 三指标的数据来源,形成检查矩阵。

工作负载刻画(Workload Characterization)回答"负载是什么"。四个维度:(1)速率——每秒多少请求/事务/I/O,及其随时间的变化曲线;(2)每请求的工作量——一次 API 调用触发多少 SQL、多少逻辑读、多少字节传输;(3)分布——请求大小的分布(是均匀的还是长尾的)、热点分布(20% 的 key 承载 80% 的流量?)、读写比例;(4)来源——哪些客户端、哪些接口贡献了负载。刻画的目的是建立"负载-性能"的因果链:性能变化要么因为系统能力变了(代码、配置、硬件、干扰),要么因为负载变了(量、分布、模式)。排障时先问"负载变了吗"往往事半功倍。工具上,应用日志聚合(按接口统计 QPS 与延迟分布)、数据库的查询统计(pg_stat_statements、MySQL performance_schema)、网络层抓包分析(tshark 统计流)都是刻画手段。

科学方法在性能分析中的操作化:观察 → 假设 → 预测 → 实验 → 结论。观察要量化("P99 从 100ms 涨到 800ms,始于 14:03"而非"变慢了");假设要具体可证伪("14:00 上线的 v2.3 引入的正则回溯导致 CPU 热点");预测要可检验("如果是正则问题,perf record 应显示 pcre_exec 占比高,且回滚后 P99 恢复");实验要控制变量(金丝雀实例回滚,其余保持)。最常见的违反是"霰弹枪式排障":同时重启服务、清缓存、扩容,问题"好了"但不知道是哪个操作起效,下次复发依旧抓瞎。另一个违反是确认偏误:认定是数据库问题后,只采集支持该假设的数据。Gregg 建议把排障过程写成时间线笔记:每一步的假设、执行的命令、观察结果、结论——这既是科学方法的执行记录,也是事后复盘与知识沉淀的素材。

反模式之一:街头灯柱效应与怪罪他人。街头灯柱效应的本质是"用工具的可用性代替问题的相关性"——只会用 top 的人看什么都是 CPU 问题。解药是掌握全栈工具矩阵(本书第 4 章)与 USE 清单(穷举检查)。"怪罪他人"(blame elsewhere)是它的孪生兄弟:没有证据就断言"是网络的问题""是云平台的问题",把球踢出去。Gregg 在 Sun/Netflix 支持团队见过太多这样的案例——最终数据表明 90% 以上被怪罪到网络/存储/云平台的问题根因在应用自身。职业化的做法是:给出证据再移交("sar -n DEV 显示网卡丢包计数持续增长,且交换机侧端口有 CRC 错误,请网络组跟进"),而不是给出猜测。

反模式之二:无基准的优化与过早优化。"优化后快了 30%"——比什么快?和谁比?在什么负载下?没有基准(baseline)的优化声明毫无意义,甚至可能引入回归而不自知。正确流程:优化前在受控环境跑基准测试记录指标 → 实施单一变更 → 同样条件重跑 → 对比差异是否超出噪声范围(多次运行取分布,见第 11 章)。另一面是 Knuth 的名言"过早优化是万恶之源":在没有剖析数据证明瓶颈位置之前,凭直觉优化代码,往往把时间花在无关紧要的地方,还牺牲了可读性。但 Gregg 补充了一个平衡观点:架构级的性能决策(同步还是异步、要不要缓存、数据分片策略)必须在设计期做,这些不是"过早优化"而是"性能设计"。区分微观优化(函数级,靠剖析驱动)与宏观设计(架构级,靠容量模型驱动)。

反模式之三:把相关当因果,以及"在错误的层次解决问题"。两个指标同时变化不代表因果关系:CPU 使用率与延迟同时上升,可能都是 QPS 上涨的共同结果;也可能 CPU 上升是延迟上升的原因(饱和排队);还可能是延迟上升导致请求堆积、进而 CPU 上升(反向因果)。区分方法是干预实验:限流降低 QPS 后 CPU 与延迟如何各自变化?层次错误的例子:应用每次请求都新建数据库连接,导致数据库连接数暴涨、性能下降,DBA 调大 max_connections 治标不治本,正解是应用加连接池。Gregg 的钻取分析法(drill-down analysis)帮助定位正确层次:系统级指标异常 → 哪个进程 → 哪个线程 → 哪个函数/系统调用/资源,逐层下钻直到找到可以行动的层次。

问题画像(Problem Characterization):接到性能问题时的前十个问题。(1)具体什么变慢了——是延迟、吞吐还是错误率?(2)从什么时候开始?(3)之前有过吗?(4)最近有什么变更——发布、配置、流量、基础设施?(5)影响范围——全部用户还是部分?全部接口还是个别?(6)系统监控图表显示什么——先看大盘再看细节;(7)负载变了吗——QPS、数据量、请求模式;(8)有没有相关的告警与日志;(9)尝试过什么操作、结果如何;(10)有没有可用的对照——健康实例、历史同期、测试环境。这十个问题平均能在 5 分钟内把问题空间缩小 80%。特别是"变更关联":绝大多数突发性能问题都由某个变更触发,把问题开始时间与变更时间线(发布记录、配置变更、cron 任务、流量活动)对齐,常常直接锁定嫌疑。

时间维度的分析技巧:趋势、周期与突变。性能数据的时间序列藏着大量信息:缓慢劣化的趋势指向资源泄漏(内存泄漏、fd 泄漏、表膨胀)或数据量增长;周期性波动指向定时任务(cron、日志轮转、备份窗口)或业务周期(早晚高峰);突变指向变更或外部事件。分析工具上,对比"问题时段"与"健康时段"的同维度曲线是最直接的手段(差分思维,第 4 章的差分火焰图是它在函数级的应用)。另一个技巧是"时间对齐":把应用延迟曲线、系统资源曲线、部署事件、流量曲线叠加在同一时间轴上,因果链常常一目了然。Gregg 强调采集频率的重要性:15 秒粒度的监控会平滑掉 3 秒的毛刺,对突发问题需要秒级甚至更细的数据(本地高频记录器,见第 1 章实验)。

静态分析与动态分析的结合。静态性能分析检查与时间无关的配置状态:sysctl 参数、ulimit、挂载选项、调度器设置、应用配置、内核版本与已知 bug。它是零成本的,应该清单化、自动化——每次上线前跑一遍"性能配置审计"。Gregg 在书中给出了大量静态检查项,例如:net.core.somaxconn 与应用的 listen backlog 是否匹配、vm.swappiness 是否符合业务预期、文件系统是否用了 noatime、JVM 是否感知容器内存限制。动态分析则研究运行时行为,按开销递增排序:计数器读取(sar/vmstat,~0%)→ 采样剖析(perf record,1-5%)→ 静态追踪点(tracepoints,激活时可控)→ 动态追踪(kprobe/uprobe,事件级开销)→ 全量事件捕获(抓包、strace,可能改变系统行为)。排障策略是"静态先行、动态由粗到细、高开销工具限时限域"。

资源画像矩阵:把 USE 方法落地为一张可执行的表。以 Linux 为例——CPU:利用率 mpstat/%usr+%sys、饱和度 vmstat r 列与 PSI cpu、错误 perf -e machine-checks;内存容量:利用率 free/MemAvailable、饱和度 si/so 与 PSI memory、错误 dmesg EDAC;内存带宽:利用率 perf -e LLC-load-misses 估算、饱和度 内存带宽计数器(Intel MBM);存储 I/O:利用率 iostat %util、饱和度 aqu-sz 与 await 与 PSI io、错误 smartctl/dmesg;存储容量:利用率 df、饱和度 无(满了就是错误)、错误 写入失败日志;网络:利用率 sar -n DEV 对比链路带宽、饱和度 netstat -s 溢出与重传、错误 ip -s link 的 errors/dropped;软件资源如前所述。建议把这张表做成 runbook,附上每条指标的具体命令与健康阈值,新工程师照着执行就能完成系统级分诊。

方法论的整合:一个完整的排障决策流程。第一步,问题画像(十个问题,5 分钟);第二步,看监控大盘,对齐时间线找变更(5 分钟);第三步,登录代表性实例跑 60 秒清单(1 分钟)+ USE 矩阵扫描(10 分钟),锁定可疑资源;第四步,工作负载刻画,确认负载侧是否变化(10 分钟);第五步,形成 2-3 个竞争性假设,用钻取分析(进程→线程→函数/事件)与追踪工具逐一验证(时长视问题而定);第六步,实施修复并用基准对比验证效果;第七步,写复盘报告,把新发现的检查项沉淀进清单。整个流程的关键性质:每一步都在缩小假设空间,任何一步的产出都是"证据"而非"感觉"。这个流程适用于从单机到分布式的各种规模,分布式场景下在第三步之前增加"定位问题服务"(用分布式追踪与黄金信号:延迟、流量、错误、饱和度)。

代码示例

vmstat 1
/* 观察 r 列(运行队列)与 b 列(阻塞队列)判断 CPU 与 I/O 饱和 */
mpstat -P ALL 1
/* 每核利用率,发现单核热点与 %steal */
pidstat -t 1
/* 按线程看 CPU,定位具体线程 */
iostat -xz 1
/* %util、await、aqu-sz:存储 USE 三件套 */
sar -n DEV 1
/* 网卡吞吐对比带宽:网络利用率 */
sar -n EDEV 1
/* 网卡错误与丢包:网络 Errors */
netstat -s | egrep -i "listen|overflow|retrans"
/* TCP 队列溢出与重传:网络饱和度 */
free -m; cat /proc/pressure/*
/* 内存利用率与三大资源的 PSI 饱和度 */
这组命令是 USE 方法在 Linux 上的速查实现,覆盖四大硬件资源,全部低开销可在生产执行。使用逻辑:vmstat 1 先看全局——r 列持续大于核数是 CPU 饱和,b 列非零是有任务阻塞在 I/O,si/so 非零是内存饱和;mpstat -P ALL 检查是否单核饱和(整机利用率不高但某核 100%,常见于单线程瓶颈或中断集中)以及云环境的 %steal;pidstat -t 1 的 -t 下钻到线程级,找出消耗 CPU 的具体线程(结合 jstack/pstack 可映射到代码);iostat -xz 的三列对应存储 USE:%util 利用率、aqu-sz 饱和度(队列深度)、await 延迟佐证,注意 NVMe 多队列设备 %util 会低估饱和;sar -n DEV 的 rxkB/s、txkB/s 对比网卡额定带宽(ethtool eth0 查看)得网络利用率,rxpck/s 对比 pps 上限;sar -n EDEV 看 rxdrop/txdrop 错误;netstat -s 中 "times the listen queue of a socket overflowed" 是 accept 队列溢出(应用来不及 accept 或 backlog 太小),"SYNs to LISTEN sockets dropped" 是半连接队列溢出,retrans 段是重传率——这三个是 TCP 层饱和度的核心计数;PSI 文件给出内核统一口径的饱和度。建议把这组命令写成 use-check.sh 脚本,故障时一键执行并保存输出。
#!/bin/bash
/* use_check.sh - USE 方法快速扫描脚本 */
echo "=== CPU ==="
mpstat 1 3 | tail -2
echo "run-queue:"; vmstat 1 3 | awk "NR>7 {print $1}"
echo "=== MEMORY ==="
free -m | head -2
grep -E "psi" /proc/pressure/memory
echo "=== DISK ==="
iostat -xz 1 3 | grep -v "^$" | tail -10
echo "=== NETWORK ==="
sar -n DEV 1 3 | grep -E "IFACE|eth"
nstat -az | grep -Ei "TcpExtListenOverflows|TcpRetransSegs"
echo "=== ERRORS ==="
dmesg -T | tail -5
这个脚本把 USE 扫描自动化,体现了"清单化"的方法论思想。几个实现细节值得注意:vmstat 输出用 awk 提取第 1 列(r 运行队列);内存部分直接读 PSI 文件,比解析 si/so 更直接;磁盘用 iostat -xz 采样 3 次取后段(第一次输出是开机以来的均值,无参考价值——这是 iostat/vmstat 类工具的通用陷阱:首个采样区间是自上次启动或上次调用以来的累积平均,必须丢弃);网络部分用 nstat -az 替代 netstat -s,nstat 输出的是计数器原始值且格式稳定,TcpExtListenOverflows 非零增长说明 accept 队列持续溢出,TcpRetransSegs 与 TcpOutSegs 的比值是重传率(健康网络应低于 0.1%,超过 1% 说明网络或主机有严重问题);最后 dmesg 扫内核错误。方法论要点:脚本的价值不在命令本身,而在"强制穷举"——人会偷懒跳过不熟悉的资源,清单不会。把这个脚本纳入你的排障 runbook,并随着踩坑不断添加检查项(比如后来你发现 conntrack 表满导致过事故,就加一行 sysctl net.netfilter.nf_conntrack_count)。
perf record -F 99 -a -g -- sleep 30
perf report --stdio | head -40
/* 或者生成火焰图 */
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > cpu.svg
这组命令展示"假设验证"阶段的采样剖析,是钻取分析的核心工具。perf record -F 99 以每秒 99 次的频率采样所有 CPU(-a)上的调用栈(-g),持续 30 秒——选 99 而不是 100 是为了避免与系统中其他每秒整点触发的定时任务产生锁频共振(采样混叠),这是 Gregg 的经典技巧。30 秒 × 99Hz × N 核的样本量足以让占比 1% 以上的热点函数统计显著。perf report 交互式浏览热点;--stdio 输出文本便于保存对比。火焰图三件套:perf script 导出原始栈 → stackcollapse-perf.pl 折叠为"栈;帧;帧 count"格式 → flamegraph.pl 渲染 SVG。火焰图的读法:X 轴是按字典序排列的样本占比(不是时间顺序),Y 轴是栈深度,宽度代表 CPU 时间占比——找"最宽的平顶"就是热点。方法论上,火焰图回答的是"CPU 时间花在哪"这个假设检验问题:如果你假设"正则回溯导致 CPU 高",火焰图上 pcre 相关帧应该占据显著宽度;如果没有,假设被证伪,换下一个。注意采样剖析只能看到 on-CPU 时间,如果 CPU 使用率很低但延迟高,需要 off-CPU 分析(第 4 章)。
strace -c -p $(pgrep -f myapp) -f
/* 统计系统调用分布,Ctrl-C 结束 */
strace -T -e trace=read,write,connect -p $(pgrep -f myapp) 2>&1 | head -50
/* -T 显示每个调用的耗时 */
strace 是"钻取到系统调用层"的经典工具,但也是开销陷阱的典型教材。-c 模式统计各系统调用的次数、总耗时、错误数,快速回答"这个进程在系统调用层面做什么"——比如你发现 connect 调用次数异常高,就验证了"没有连接复用"的假设;read/write 的 errors 列非零提示 I/O 问题。-T 模式给每个调用附加耗时,可以定位"哪个系统调用慢"。但必须警告:strace 基于 ptrace 机制,每次系统调用都要陷入-停止-恢复目标进程两次,对系统调用密集的程序(数据库、代理)可造成 10 倍到 100 倍的减速——在生产环境对关键进程 strace 可能直接把服务打挂。方法论上的正确用法:(1)优先用低开销替代品——strace -c 的功能可用 perf trace 或 bpftrace 的 syscalls 追踪实现,开销低一个数量级;(2)必须用 strace 时,限定范围(-e trace= 只追踪关心的调用)、限定时间(timeout 10 strace ...)、选择非关键实例(金丝雀);(3)意识到观测效应——strace 之下"慢"可能是 strace 自己造成的。这个例子深刻说明:方法论不仅包括"用什么工具",更包括"以什么代价、在什么范围用"。
bpftrace -e "tracepoint:syscalls:sys_enter_connect { @[comm] = count(); }"
/* 统计谁在发起 TCP 连接,按进程名聚合 */
bpftrace -e "kprobe:tcp_retransmit_skb { @retrans[comm] = count(); }"
/* 统计 TCP 重传的来源进程 */
这两条 bpftrace 单行命令展示了动态追踪如何以极低开销回答精确的方法论问题。第一条挂载在 syscalls:sys_enter_connect 追踪点上,每当任何进程调用 connect() 就按进程名(comm)计数——如果你怀疑"重试风暴"或"连接未复用",跑 10 秒就能看到哪个进程在疯狂建连,这比抓包分析轻量得多。第二条挂载 kprobe 到内核函数 tcp_retransmit_skb,统计每个进程触发的重传次数,直接回答"重传是谁造成的"——传统工具(netstat -s)只能给全局重传计数,无法归因到进程,这就是动态追踪的钻取价值。方法论要点:假设驱动的工具选择。先有假设("延迟抖动来自某进程的重传"),再选择能以最小开销验证该假设的工具(bpftrace 按进程聚合重传),预期结果明确(如果假设成立,@retrans 中某进程计数显著;否则证伪)。bpftrace 的开销与被追踪事件的频率成正比,connect 与重传都是低频事件,因此这两条命令在生产环境安全。对比之下,如果追踪 sys_enter_read 这种每秒百万次的事件,就需要加采样或聚合控制。

实验笔记

实验二:USE 方法实战演练——系统化诊断一个人造的"疑难杂症"。

实验目标:(1)把 USE 矩阵落地为可执行脚本;(2)在不知道根因的情况下,纯靠方法论定位多个植入的性能问题;(3)体会"穷举检查"相对"直觉猜测"的优势。

环境准备:Linux 虚拟机(2 核 4GB 起),安装 sysstat、stress-ng、perf(linux-tools)、bpftrace(内核 4.9+,Ubuntu 20.04 自带 5.x 内核可直接 apt install bpftrace)。准备一个"故障注入脚本" fault.sh(自己编写或用同学互相出题),内容从以下故障库中随机选 2-3 个后台运行:(a)CPU 密集死循环进程(模拟应用热点);(b)stress-ng --vm-bytes 制造内存压力与 swap 抖动;(c)stress-ng --hdd 制造磁盘饱和;(d)一个每秒建立数百个 TCP 连接的脚本(模拟连接风暴,用 bash 循环 curl 或 python socket);(e)用 tc netem 给网卡注入 50ms 延迟与 1% 丢包(模拟网络劣化,需要 root:tc qdisc add dev eth0 root netem delay 50ms loss 1%);(f)把 somaxconn 调小到 10 并起一个高并发 HTTP 服务(模拟配置错误导致 accept 队列溢出)。

步骤一(编写 USE 扫描脚本):参考本章代码示例,编写 use_check.sh,要求覆盖:CPU(每核利用率、运行队列、PSI)、内存(available、swap 活动、PSI)、每块磁盘(%util、await、aqu-sz、PSI io)、网络(每网卡吞吐、错误丢包、TCP 溢出与重传计数)、常见软件资源(fd 使用量 top 进程、conntrack 使用率、监听队列溢出)。输出带时间戳保存到 report_$(date +%s).txt。

步骤二(盲测诊断):让同伴运行 fault.sh 注入故障(不告诉你注入了什么),你只允许使用 use_check.sh、60 秒清单、以及任何你认为必要的低开销工具,在 15 分钟内写出诊断报告:每个异常资源的 USE 三指标证据、你推断的根因、置信度。规则:禁止直接 ps 找 stress-ng(那等于作弊),必须从资源指标反推。

步骤三(钻取验证):对诊断出的每个问题,用钻取分析确认到进程/线程级:CPU 热点用 pidstat -t 找到线程再用 perf record 看函数;连接风暴用 bpftrace 的 connect 计数脚本归因到进程;队列溢出用 nstat 确认 TcpExtListenOverflows 持续增长并找到对应监听端口(ss -lnt 查看 Recv-Q 与 Send-Q)。

步骤四(修复与验证):针对每个根因实施修复(kill 热点进程、调整 somaxconn、去掉 tc netem:tc qdisc del dev eth0 root),重跑 use_check.sh 确认指标回归基线。

预期结果:你能对每个注入的故障给出"资源 → USE 指标 → 证据数据 → 根因"的完整证据链。特别地,故障(f)配置类问题最考验方法论——CPU/内存/磁盘全部正常,唯一的线索藏在 netstat -s 的溢出计数里,这正是"穷举清单"胜过"直觉猜测"的例证。

进阶挑战:(1)给故障注入加上"伪装":把 stress-ng 改名并降低单进程占用,看你的钻取分析是否还能定位;(2)同时注入 CPU 饱和与网络丢包,练习区分两个并发问题各自的延迟贡献(提示:off-CPU 分析);(3)把你的诊断过程写成时间线笔记(假设-命令-观察-结论四栏),对照科学方法检查每一步是否有明确目的;(4)为 use_check.sh 增加健康阈值判断,输出 PASS/WARN/FAIL,做成可自动化的巡检工具。

延伸阅读

  • Brendan Gregg《The USE Method》(https://www.brendangregg.com/usemethod.html)。USE 方法的官方页面,包含方法定义、各操作系统(Linux、Solaris、macOS、Windows)的 USE 清单、以及针对物理资源与软件资源的完整检查矩阵。页面附的 Linux USE Checklist 是本章实验脚本的权威参照,建议逐条对照补全自己的 runbook。
  • Brendan Gregg《Linux Performance Analysis in 60s》(Netflix Tech Blog, 2015)。60 秒清单的原始文章,附有一个真实排障案例的完整演示:如何用这 10 条命令在 60 秒内发现磁盘 I/O 饱和。文章评论区还有大量从业者的补充经验,是理解"清单式分诊"价值的最佳入门读物。
  • 《Systems Performance》第 2 版第 2 章 Methodologies 原文。原书系统展开了 USE、工作负载刻画、科学方法、问题画像、钻取分析、事件追踪、基准测试、静态分析等十余种方法论,并逐一讨论了反模式(街头灯柱、随机变更、怪罪他人等)。本章笔记是压缩版,原书的每个方法论都配有真实案例对话,值得细读。
  • John Allspaw《The Art of SLOs》与 Google SRE Workbook 的 SLO 章节。方法论不仅是排障技术,还包括如何定义"什么算慢"。SLO/错误预算框架把性能目标变成工程与业务的共同语言,是"问题画像"第一步(量化现象)的制度保障。Allspaw 的演讲还深入讨论了事故复盘的免责文化,与本章"怪罪他人"反模式直接呼应。
  • Thomas Kuhn《科学革命的结构》中关于范式与常规科学的讨论,以及 Karl Popper 的证伪主义简介(任何一本科学哲学入门读物均可)。性能分析的科学方法并非 Gregg 发明,而是科学方法论在工程领域的应用。理解"假设必须可证伪""实验必须控制变量"的哲学基础,能帮你在排障压力下保持思维纪律,避免滑向随机变更法。
  • Brendan Gregg《Systems Performance Methodologies》配套的思维导图与 Linux 性能工具全景图(https://www.brendangregg.com/linuxperf.html)。这张著名的全景图按"观测目标 × 工具"组织上百个 Linux 工具,与 USE 矩阵互为经纬:USE 告诉你查什么,全景图告诉你用什么查。建议动手把两者合并成自己的一页纸速查表。
  • Dan Slimmon《The Streetlight Effect》相关博文与认知偏误清单(如维基百科 List of cognitive biases 中的 confirmation bias、anchoring)。性能排障是高强度认知活动,工程师在时间压力下极易落入认知偏误。了解这些偏误的表现形式(锚定于第一个假设、只采集支持性证据),并建立对抗机制(写下竞争性假设、要求证伪性实验),是方法论修养的深层部分。
2/13性能方法
0%