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

系统性能概述

为什么性能如此重要

响应时间吞吐量延迟性能角色

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

第1章 系统性能概述

为什么性能如此重要

章节导读

当我们谈论"系统性能"时,不同的人脑海中浮现的画面截然不同。对最终用户来说,性能是点击按钮后页面多久出现;对运维工程师来说,性能是服务器 CPU 用了多少、磁盘忙不忙;对架构师来说,性能是整个系统在峰值流量下能否撑住;对 CFO 来说,性能直接换算成机器成本和营收损失。Brendan Gregg 在本书开篇就指出:性能是一个多视角的问题,任何单一指标都无法完整刻画一个系统的性能状况。理解这一点,是踏入性能分析领域的第一步。

让我们从一个真实的故事开始。2012 年,某大型电商在促销季前夕上线了新版本,一切测试通过,但上线后订单接口的 P99 延迟从 200ms 飙升到 3s。团队第一反应是回滚,回滚后问题消失;再次上线,问题复现。折腾三天后,一位资深工程师用动态追踪工具发现:新版本引入的一个日志库在每条日志上都调用了 fsync,而促销流量下每秒数万条日志意味着每秒数万次磁盘强制刷新,存储子系统彻底饱和。这个案例浓缩了性能工程的三大教训:第一,平均数会骗人,P99 才是用户真实的痛感;第二,测试环境与生产环境的差异会掩盖问题;第三,没有系统性的观测手段,排查就是盲人摸象。

为什么性能如此重要?首先是用户体验与营收的直接关联。Google 的经典研究表明,搜索延迟每增加 400ms,用户搜索量下降 0.6%;Amazon 报告页面加载每慢 100ms,销售额下降 1%。在移动互联网时代,用户的耐心以毫秒计。其次是成本。同样的业务量,性能好的系统可能只需要一半的机器。在云计算按量付费的模式下,性能优化直接等于省钱——一次成功的 CPU 热点优化,可能让集群规模从 1000 台缩减到 600 台,每年节省数百万美元。第三是容量规划与稳定性。理解系统在各资源维度上的极限,才能预测流量增长何时会压垮服务,才能设计出合理的降级与扩容策略。性能分析不仅是"救火",更是"防火"。

性能领域有多个角色分工,理解各自的视角有助于协作。应用开发者关注代码层面的效率:算法复杂度、数据结构选择、运行时行为(如 GC)。他们通常用剖析器(profiler)在开发阶段发现问题。SRE(站点可靠性工程师)关注服务级别目标(SLO)、错误预算、容量与可用性,他们需要在故障发生时快速定位问题,工具以系统级观测为主。专职性能工程师则横跨两者,既懂应用又懂系统,负责深度调优与性能架构评审。内核与系统软件开发者的视角最底层:调度器、内存管理、I/O 栈的实现细节。Gregg 强调,现代性能问题往往跨越多个层次,一个"应用慢"的表象,根因可能在 NUMA 内存分配、TCP 重传、甚至云平台的噪声邻居。因此性能工程师必须具备"全栈"视野——这正是本书的组织逻辑:自顶向下覆盖应用、操作系统、CPU、内存、存储、网络、云。

云时代给性能分析带来了全新挑战。第一是多租户干扰:你的虚拟机与陌生人的负载共享物理 CPU、内存带宽、磁盘与网络,"噪声邻居"会让你的服务莫名其妙变慢,而你在虚拟机内部看到的指标可能完全正常。第二是弹性伸缩:实例随时创建销毁,性能数据必须与实例生命周期关联,传统的"登录机器看 top"模式失效,需要集中式可观测性平台。第三是分布式系统:一个用户请求可能穿越几十个微服务,端到端延迟的归因需要分布式追踪。第四是抽象层的性能税:容器、Serverless、托管数据库都引入了额外的抽象,理解这些抽象的性能特性成为必修课。

性能分析工具的演进史也值得一提。早期只有粗粒度的系统计数器(如 sar、vmstat,诞生于 1970-80 年代的 Unix),它们告诉你"系统忙",却不告诉你"谁在忙、忙在哪"。1990 年代出现了采样式剖析器(如 gprof、OProfile),能定位热点函数。2000 年代,Sun 公司的 DTrace 革命性地实现了生产环境零开销动态追踪,Gregg 正是在这一时期发明了火焰图。Linux 先后有 SystemTap、perf_events,但真正让动态追踪在 Linux 上普及的是 2014 年之后的 eBPF 技术——它允许把沙箱化的程序注入内核任意位置,安全地采集事件,BCC 与 bpftrace 工具链由此繁荣。本书第 2 版大幅扩充了 eBPF 与云计算内容,反映了这一技术变迁。

本章作为全书的总览,将建立性能分析的基本词汇表:延迟、响应时间、吞吐量、利用率、饱和度、性能指标的分位数、时间尺度的层次(从纳秒级的 CPU 指令到秒级的请求延迟)。后续章节将依次深入方法论(第2章)、操作系统(第3章)、工具(第4章)与各子系统(第5-10章),最后是基准测试、调优与案例研究(第11-13章)。请带着一个核心问题阅读全书:当用户说"系统慢"时,我如何用最短的路径、最低的观测开销,找到那个真正的瓶颈?

核心概念

  • 响应时间
  • 吞吐量
  • 延迟
  • 性能角色

要点精析

性能的第一对核心概念是响应时间(response time)与吞吐量(throughput)。响应时间是单个请求从发出到收到完整回复所经历的时间,也叫延迟(latency),它决定用户体验;吞吐量是单位时间内系统处理的请求总数,它决定系统容量与成本。两者经常此消彼长:批处理可以提高吞吐量(比如磁盘 I/O 合并、网络包聚合),但会增加单个请求的等待时间。排队论告诉我们,当系统利用率接近 100% 时,响应时间会呈非线性爆炸式增长——利用率从 70% 升到 90%,排队延迟可能增加数倍。这就是为什么生产系统通常把资源利用率红线设在 70-80%。一个常见误区是只看吞吐量做容量规划:某服务单机 QPS 能到 5000,但那是以 P99 延迟 2s 为代价的压测极限值,真实可用的容量可能只有 2000 QPS。正确的做法是同时约束吞吐量与延迟 SLO,例如"P99 < 200ms 前提下的最大 QPS"。

延迟必须用分布而非平均值来描述。平均延迟会掩盖长尾:如果 99% 的请求 10ms 完成,1% 的请求 2s 完成,平均值只有约 30ms,看起来很美,但那 1% 的用户体验是灾难性的,而且大规模系统中每个用户都会频繁遭遇长尾。因此业界标准做法是报告分位数(percentile):P50(中位数)、P90、P99、P99.9。分位数的一个陷阱是不可直接平均——两台机器的 P99 取平均并不等于整体的 P99,正确做法是用直方图(histogram)合并原始分布再计算分位数,这也是HdrHistogram 等库存在的原因。另一个重要概念是长尾放大效应:一个用户请求若扇出调用 100 个后端服务,只要每个后端有 1% 的概率慢,该请求遭遇至少一次慢调用的概率高达约 63%(1 - 0.99^100)。这就是 Jeff Dean 在"大规模分布式系统的长尾"演讲中强调的问题,解法包括对冲请求(hedged requests)与绑定请求(tied requests)。

时间尺度层次是性能分析的基本功。Jeff Dean 广为流传的"每个程序员都该知道的延迟数字"给出了直观标尺:L1 缓存访问约 0.5ns,分支预测失败约 5ns,L2 缓存约 7ns,互斥锁加解锁约 25ns,主存访问约 100ns,用 1Gbps 网络发送 2KB 约 20μs,从内存读 1MB 约 250μs,磁盘寻道约 10ms(HDD),从 SSD 随机读约 150μs,用 1Gbps 网络传输 1MB 约 10ms,磁盘顺序读 1MB 约 20-30ms。这些数字随硬件演进在变化(NVMe SSD 随机读已到 10-20μs 量级),但相对关系依然成立:内存比 SSD 快三个数量级,SSD 比 HDD 快两个数量级,跨数据中心往返比本机内存慢五个数量级。性能优化的本质常常是"把操作往时间尺度表的上方移动":用缓存避免磁盘访问、用批量摊薄网络往返、用无锁结构避免互斥锁。分析问题时先估算理论下界,如果实测延迟远高于下界,中间一定有可解释的开销。

性能问题的四大常见来源:资源饱和、配置错误、代码缺陷、架构问题。资源饱和指某资源(CPU、内存、磁盘、网络)达到容量上限,请求开始排队,特征是利用率接近 100% 且伴随饱和度指标(运行队列长度、swap 活动、I/O 等待)升高。配置错误是最常见也最容易被忽视的一类:文件描述符上限太低导致连接被拒、TCP 缓冲区默认值不适配高带宽长肥管道、JVM 堆大小与容器内存限制不匹配导致 OOM Kill、连接池太小造成排队。代码缺陷包括低效算法(O(n²) 循环)、N+1 查询、锁争用、内存泄漏。架构问题最深层:同步阻塞的调用链、单点数据库、缺少缓存层、跨地域的同步复制。Gregg 的经验法则是:先查配置(成本最低、命中率最高),再看资源饱和,然后剖析代码,最后反思架构。

利用率(Utilization)、饱和度(Saturation)、错误(Errors)是描述资源状态的三元组,也是第 2 章 USE 方法的雏形。利用率是资源忙碌时间的百分比,如 CPU 使用率 90%;但高利用率不一定有问题——CPU 90% 忙碌且请求都快速完成是健康的高负载。饱和度是资源超出处理能力后请求排队的程度,如运行队列长度、I/O 队列深度、swap in/out 速率,饱和度大于零通常直接意味着性能受损。错误是硬件事件(ECC 内存纠错、磁盘坏道重映射、网卡 CRC 错误)与软件错误(丢包、OOM)。三者的关系:利用率是领先指标,饱和度是确认指标,错误往往是硬件劣化的信号。一个完整的资源检查必须三者兼顾,只看 CPU 使用率会漏掉"使用率 60% 但调度延迟严重"(如 CPU 被限流的容器)这类问题。

性能分析中要区分"性能"(performance)与"可伸缩性"(scalability)。性能是单实例或固定资源下系统处理工作的效率;可伸缩性是增加资源(加 CPU、加机器)后性能提升的能力。理想情况下性能随资源线性增长,但现实中会遇到 Amdahl 定律的制约:如果程序有 10% 的串行部分,那么无论加多少核,加速比上限都是 10 倍。更糟的是负伸缩:加核反而变慢,典型原因是锁争用(核越多,抢锁越激烈)、缓存一致性流量(多核间缓存行乒乓)、内存带宽饱和。诊断可伸缩性问题的标准做法是绘制"吞吐-并发"曲线与"延迟-并发"曲线:健康系统吞吐先线性上升后平缓,延迟缓慢上升后在拐点处陡增;有争用的系统吞吐会在某个并发数后不升反降。USL(Universal Scalability Law)模型可以拟合出争用系数与一致性开销系数,预测系统极限。

观测本身会影响被观测对象,这是性能分析特有的"测不准问题"。工具开销从低到高大致为:读取内核计数器(近乎为零)、采样式剖析(1-5%)、静态追踪点(激活时每个事件约 0.5-2μs)、动态追踪与高频事件计数(可能 10% 以上)、strace 这类基于 ptrace 的系统调用追踪(对系统调用密集的程序可造成 100 倍以上的减速)。生产环境排障的黄金准则是:优先使用低开销工具(计数器、采样),需要高精度追踪时控制范围(限定 PID、限定事件、限定采样率)与时长。Gregg 提出的开销评估方法是"基准对比":开启工具前后分别跑同样的负载,测量吞吐与延迟差异。此外要警惕观测盲区:某些问题只在工具关闭时出现(海森堡 bug),此时需要用常驻的低开销观测(如 always-on 的 60 秒计数器采集)加上事后分析。

云时代的性能分析面临"看不见的楼下"问题。在传统物理机上,从应用到硬件的每一层都可观测;在云上,Hypervisor、物理网卡、共享存储后端对租户是黑盒。典型症状:虚拟机内 CPU 使用率只有 30%,应用却很慢——用 mpstat 看到 %steal 高达 20%,说明 Hypervisor 把物理 CPU 时间分给了别的租户。容器环境有类似但不同的问题:cgroups CPU 配额限流(throttling)会造成周期性的延迟毛刺,容器内 top 看到的核数可能是宿主机的核数而非配额,导致线程池配置错误。云磁盘的性能是按容量或购买档位分配的(如 EBS gp3 基线 3000 IOPS),小容量盘性能低得惊人,且突发额度耗尽后性能断崖式下跌。方法论上的调整是:先确认云平台的资源规格与限制(这些是"配置"),再在实例内部做常规分析,必要时借助云厂商提供的宿主机侧指标。

性能角色分工决定了工具选择与协作方式。应用开发者在开发/测试环境用剖析器(perf、async-profiler、py-spy)做 CPU 与内存热点分析,关注函数级效率;SRE 在生产环境用系统级工具(top、vmstat、iostat、sar)与集中式监控(Prometheus、Grafana)做快速分诊,关注 SLO 与容量;性能工程师做深度分析,动用动态追踪(bpftrace、BCC)与火焰图,横跨应用与内核;平台/内核工程师则可能修改内核参数甚至打补丁。Gregg 强调"性能团队"的组织模式有两种:集中式专家小组(深度强、易成瓶颈)与嵌入式工程师(响应快、深度依赖个人)。无论哪种模式,有效的性能工作需要三类产出:解决具体问题(救火)、建立工具与平台(赋能)、沉淀方法论与培训(文化)。个人成长路径上,建议先精通一个子系统(如存储),再横向扩展到全栈。

性能指标分为静态与动态两类,对应的工具也不同。静态性能分析工具检查与时间无关的配置与状态:内核参数(sysctl)、文件描述符限制(ulimit)、磁盘挂载选项、网卡队列配置、应用配置文件。它们开销为零,适合在排障初期快速过一遍——大量性能事故的根因就是一个错误的配置。动态性能分析工具研究系统随时间的行为:计数器(累积统计,如 /proc/stat)、剖析(采样,如 perf record)、追踪(事件级,如 bpftrace)。Gregg 的建议是把静态检查清单化:为新服务上线建立"性能配置审查清单",把历史上踩过的坑(如 somaxconn 太小、JVM 未感知容器限制)固化为自动检查项。动态分析则遵循"先粗后细":先用 60 秒检查清单扫一遍全局计数器,锁定可疑资源,再用剖析与追踪深入。

建立性能分析的"全栈思维"意味着遇到问题时能在脑中展开一张从应用到硬件的完整地图。以一次 Web 请求为例:浏览器发起 DNS 查询(DNS 服务器、缓存层次)→ TCP 三次握手(内核网络栈、SYN 队列)→ TLS 握手(CPU 密集的非对称加密)→ 请求到达应用(线程池、事件循环)→ 业务逻辑(CPU、内存分配、GC)→ 查询数据库(连接池、网络往返、数据库缓冲池、磁盘 I/O)→ 响应返回(TCP 发送缓冲、网卡)。每一层都有自己的延迟特征、饱和模式与观测工具。全栈思维的价值在于快速排除:如果 iostat 显示磁盘空闲、mpstat 显示 CPU 空闲,那延迟大概率在网络往返或锁等待上,可以直接跳到对应工具。本书后续章节就是这张地图的逐层放大,学完你应该能对任何"系统慢"的报告,在几分钟内说出"瓶颈在哪一层、证据是什么"。

最后要树立正确的性能工作观:性能是特性,不是事后补救。事后优化的成本远高于设计阶段的性能考量——架构定型后再想加缓存层、改异步化,往往伤筋动骨。业界成熟的做法包括:把延迟 SLO 写进需求(如"P99 < 100ms");上线前做性能基准测试并纳入 CI(性能回归检测);生产环境常驻低开销观测(计数器 + 按需剖析),保证问题发生时"有案可查";重大变更做金丝雀发布并对比性能指标。同时要接受"性能工作的边际递减":优化到 SLO 达标即可停止,过度优化浪费工程资源。Gregg 还提醒警惕"局部优化陷阱"——把 90% 的时间花在只占 5% 开销的模块上。永远先测量、找到最大的瓶颈(Amdahl 定律视角),再动手优化,优化后再测量验证。这个"测量-优化-验证"的循环贯穿本书,也是第 12 章调优方法论的核心。

代码示例

uptime
dmesg -T | tail -20
vmstat 1 5
mpstat -P ALL 1 3
pidstat 1 3
iostat -xz 1 3
free -m
sar -n DEV 1 3
sar -n TCP,ETCP 1 3
top
这是 Gregg 著名的"Linux 60 秒检查清单",出自他在 Netflix 的实战经验,也是本章最重要的命令组。逐条解释:uptime 输出负载平均值(1/5/15 分钟),三个数字的趋势告诉你负载在上升还是回落;dmesg -T 查看内核日志尾部,寻找 OOM killer、网卡 up/down、磁盘错误、TCP 内存压力等关键事件,-T 把内核时间戳转为人类可读时间;vmstat 1 5 每秒采样一次系统全局状态,重点看 r 列(运行队列长度,持续大于 CPU 核数说明 CPU 饱和)、si/so(swap 换入换出,非零说明内存不足)、wa(I/O 等待);mpstat -P ALL 按 CPU 核显示使用率,能发现单核热点(某个核 100% 而其他核空闲,常见于中断亲和不均或单线程瓶颈);pidstat 1 按进程显示 CPU 使用,定位"是谁在吃 CPU";iostat -xz 1 3 的 -x 给出扩展指标,重点看 %util(设备忙碌度)与 await(I/O 平均延迟),-z 隐藏空闲设备;free -m 看内存与缓存的总体分布,注意 available 列才是应用真正可用的内存;sar -n DEV 看网卡吞吐是否逼近带宽上限;sar -n TCP,ETCP 看 TCP 连接速率与错误(retrans、active/passive 失败);最后 top 综合确认。整套清单 60 秒内跑完,覆盖了 CPU、内存、磁盘、网络四大资源,是任何 Linux 性能排查的标准开局。
cat /proc/loadavg
cat /proc/stat | head -5
cat /proc/meminfo | head -10
cat /proc/net/dev
cat /proc/pressure/cpu
cat /proc/pressure/io
cat /proc/pressure/memory
这组命令展示 Linux 内核暴露的原始性能接口,全部是静态读取、零开销,适合脚本化采集。/proc/loadavg 前三列是 1/5/15 分钟负载均值,第四列是"运行中进程数/总进程数",第五列是最近创建的 PID——负载均值在 Linux 上包含不可中断睡眠(D 状态)的进程,因此高负载不一定等于高 CPU,可能是在等 I/O。/proc/stat 首行 cpu 后的数字是各状态累计 jiffies:user、nice、system、idle、iowait、irq、softirq、steal(被 Hypervisor 偷走的时间,云环境重点指标)、guest。/proc/meminfo 给出内存全景:MemTotal、MemFree、MemAvailable(内核估算的可用量,比 MemFree 更有意义)、Buffers、Cached、Slab、SwapTotal/SwapFree、Dirty(待回写的脏页,过大说明回写压力大)。/proc/net/dev 按网卡列出收发包数与字节数及错误、丢包计数。最有价值的是 /proc/pressure/*(PSI,Pressure Stall Information,Linux 4.20+):它直接量化"任务因等待该资源而停滞的时间比例",some 行表示至少一个任务在等待,full 行表示所有任务都在等待(CPU 没有 full 行)。PSI 是饱和度的一手指标,比从利用率间接推断更准确,cgroups v2 与 Facebook 的 OOM 决策(oomd)都基于它。
perf stat -a sleep 10
perf stat 是 Linux 性能计数器的一站式入口,-a 表示统计所有 CPU 上的系统级事件,sleep 10 定义 10 秒的观测窗口。输出包括:task-clock(消耗的 CPU 时间)、context-switches(上下文切换次数,每秒数万次以上可能说明过度调度或锁争用)、cpu-migrations(进程在核间迁移次数,过高伤害缓存局部性)、page-faults(缺页次数)、cycles(CPU 周期)、instructions(指令数)以及最关键的 IPC(instructions per cycle,每周期指令数)。IPC 是判断 CPU 效率的黄金指标:现代超标量 CPU 理论 IPC 可达 4,实测 IPC 大于 1.5 说明计算密集且缓存友好;IPC 低于 0.5 说明 CPU 大量时间在等待——等内存(缓存未命中)、等分支预测恢复、等锁。看到低 IPC 后,下一步用 perf stat -e cache-misses,cache-references,branch-misses 细分原因,或用 perf record 采样定位到具体函数。在云虚拟机中部分硬件计数器可能不可用(Hypervisor 未暴露 PMU),此时 IPC 显示为 not supported,可以退而用软件事件。这条命令开销接近零,可以在生产环境随时执行。
sar 1 10
sar -d 1 5
sar -r 1 5
sar -q 1 5
sar(System Activity Reporter)是历史最悠久的系统级性能工具,源自 1970 年代的 Unix,最大优势是"回溯能力":sysstat 包的采集守护进程默认每 10 分钟把系统指标写入 /var/log/sa/ 下的二进制文件,故障发生后你可以用 sar -f /var/log/sa/sa15 回看事发当时的 CPU、内存、I/O、网络状态——这是"事后取证"的关键,因为很多性能问题在工程师登录机器时已经消失。不带 -f 时 sar 显示当天从零点开始的数据。常用选项:sar 1 10 每秒采样 CPU 使用率(%user、%system、%iowait、%steal 各列含义与 /proc/stat 对应);sar -d 按块设备显示 I/O(tps、每秒扇区数、平均请求大小、await、%util);sar -r 显示内存与 swap 活动(重点看 pgscank/pgscand,即 kswapd 后台扫描与直接回收的页数,直接回收非零说明内存压力已影响应用延迟);sar -q 显示负载均值与运行队列。sar 的数据全部来自内核计数器,开销可忽略,适合作为常驻观测的基线。与 vmstat/iostat 相比,sar 的指标更全且有历史存档,两者结合使用:实时排障用 vmstat 系列,事后分析用 sar。
tsar --cpu --mem --load --io -i 1 -l
nmon -f -s 5 -c 12
这组命令代表"常驻性能记录器"的思路。tsar 是阿里巴巴开源的系统监控采集工具,以模块化方式采集 CPU、内存、负载、I/O、网络、TCP 等指标并存储历史数据,-i 1 -l 表示按 1 秒间隔实时输出;它的设计目标就是在每台机器上低开销地长期记录,出问题时快速回放。nmon(Nigel's Monitor)源自 IBM,-f 以固定间隔(-s 5 秒)采集 -c 12 次并写入 CSV 文件,之后可用 nmon_analyzer 生成 Excel 报表,适合做基准测试期间的系统侧记录。这类工具的价值在于填补"监控系统的空白":Prometheus 类监控通常 15-60 秒采集一次且只保留预定义指标,而 5 秒内转瞬即逝的毛刺(如一次 GC 停顿、一波突发 I/O)会漏掉;本地高频记录器则能捕获细粒度现场。生产实践建议:每台关键机器部署低开销的秒级采集(CPU、内存、I/O、网络、PSI),保留 7-30 天,磁盘成本极低,但在事故复盘时价值巨大——Gregg 称之为"黑匣子"模式,就像飞机失事后总有飞行数据记录器可查。

实验笔记

实验一:建立你的第一份系统性能画像。

实验目标:(1)熟练使用 60 秒检查清单;(2)理解负载均值、利用率、饱和度三者的区别与联系;(3)亲手制造并观测 CPU 饱和、内存压力、I/O 饱和三种状态,建立"指标-现象"的直觉映射。

环境准备:一台 Linux 虚拟机或物理机(建议 2 核 4GB 以上,Ubuntu 20.04+/CentOS 8+ 均可),安装工具包:sudo apt install sysstat stress-ng htop(CentOS 用 yum/dnf)。为安全起见不要在共享生产机上做压力实验。建议同时开两个终端:一个跑压力发生器,一个跑观测命令;或用 tmux 分屏。

步骤一(基线采集):在空载系统上依次执行 60 秒检查清单的全部命令,把输出保存到 baseline.txt。记录:负载均值、vmstat 的 r 列、CPU 各状态百分比、iostat 的 %util、free 的 available。空载系统这些值应接近零,这份基线是后续对比的参照。

步骤二(CPU 饱和实验):执行 stress-ng --cpu 0 --timeout 60(--cpu 0 表示按核数启动等量 worker)。同时在另一终端观察:uptime 的负载均值应在 1 分钟内爬升到约等于核数;vmstat 1 的 r 列应持续大于等于核数(这就是 CPU 饱和的直接证据——有任务在运行队列排队);mpstat -P ALL 1 显示所有核 %user 接近 100%;top 中 stress-ng 进程占据 CPU。再执行 cat /proc/pressure/cpu,观察 some avg10 接近 100。思考题:如果启动核数两倍的 worker(stress-ng --cpu $((2*$(nproc)))),负载均值会是多少?r 列呢?IPC 会如何变化(用 perf stat -a 对比)?

步骤三(内存压力实验):执行 stress-ng --vm 1 --vm-bytes 80% --vm-keep --timeout 60,让一个 worker 持续占用 80% 内存。观察 free -m 的 available 骤降;vmstat 1 的 si/so 列——如果系统配置了 swap 且压力足够,你会看到换页活动;cat /proc/pressure/memory 的 some 值上升;sar -r 中 pgscand(直接回收)非零说明应用分配内存时被迫同步回收,这会直接增加应用延迟。进阶:把 --vm-bytes 提到 95%,观察 dmesg 中是否出现 OOM killer 记录(stress-ng 的子进程可能被杀,这是预期行为)。

步骤四(I/O 饱和实验):执行 stress-ng --hdd 2 --hdd-bytes 1G --timeout 60 制造磁盘写入压力。观察 iostat -xz 1:目标设备的 %util 接近 100%,await 显著上升(HDD 上可能到几十毫秒,SSD 上几毫秒),aqu-sz(队列深度)大于 1;vmstat 的 wa(iowait)列上升;cat /proc/pressure/io 显示 I/O 压力。思考题:为什么在 NVMe SSD 上 %util 达到 100% 不一定代表饱和?(提示:NVMe 是多队列设备,可并行处理多个请求,%util 只表示"有请求在处理的时间比例",这是 iostat 指标在多队列时代的著名陷阱,第 8 章会深入讨论。)

步骤五(综合与报告):写一个 shell 脚本 snapshot.sh,一次性采集 uptime、/proc/loadavg、vmstat 1 3、mpstat、iostat、free、PSI 三项,输出带时间戳的报告。在三种压力场景下分别运行,整理成对比表格。

预期结果:你能清晰说出每种饱和状态的"指纹指标"——CPU 饱和:r 列高 + %user/%system 高 + PSI cpu 高;内存压力:available 低 + si/so 或 pgscand 非零 + PSI memory 高;I/O 饱和:%util 高 + await 高 + PSI io 高 + wa 高。

进阶挑战:(1)用 perf stat 测量三种压力下系统的 IPC 与上下文切换率,解释差异;(2)在虚拟机中观察 %steal 是否非零,理解宿主机超售的影响;(3)把 snapshot.sh 改造成每 5 秒采集一次的常驻记录器,保留 24 小时数据,然后用它回放你制造压力的时间段——体会"黑匣子"的价值;(4)阅读 /proc/pressure 的内核文档(Documentation/accounting/psi.rst),理解 PSI 的统计口径。

延伸阅读

  • 《Systems Performance: Enterprise and the Cloud, 2nd Edition》第 1 章原文(Brendan Gregg, Addison-Wesley, 2020)。本章内容即是对原书第 1 章的展开,原书还包含更详细的性能云图(Performance Cloud)、翻译问题(lost in translation)与角色模型讨论。建议对照阅读原书中"Latency"一节的时延数量级表格,并动手在自己机器上验证这些数字。
  • Jeff Dean 与 Luiz Barroso 的论文《The Tail at Scale》(Communications of the ACM, 2013)。这篇论文系统阐述了大规模分布式系统中的长尾延迟问题:扇出请求遭遇长尾的概率放大、对冲请求与绑定请求等缓解手段。它是理解"为什么 P99 比平均值重要"的最佳读物,篇幅短小但影响深远,几乎所有 SRE 必读清单都包含它。
  • Brendan Gregg 的博客 https://www.brendangregg.com/blog/ 。Gregg 持续更新性能分析前沿内容,经典文章包括《The Linux perf Tool Chain》《USE Method》《The 60 Second Checklist》的原始出处,以及每年一度的性能工具全景综述。他的博客是本书内容的"活页补充",eBPF 新工具往往最先在这里发布。
  • 《Systems Performance》官方网站 https://www.brendangregg.com/systems-performance-2nd-edition-book.html 。提供全书目录、示例图表、勘误与配套工具链接。特别推荐网站上的 Linux 性能观测工具全景图(linux_perf_tools_full.png),它把上百个工具按子系统与方法论分类在一张图上,打印贴在工位旁,是快速选工具的地图。
  • Google SRE Book(https://sre.google/sre-book/table-of-contents/),特别是第 4 章 Service Level Objectives 与第 6 章 Monitoring Distributed Systems。它从工程组织角度定义了如何用 SLO、错误预算把"性能"变成可管理、可权衡的业务指标,与本书的技术视角互补。免费在线阅读,有中文版《SRE:Google 运维解密》。
  • Peter Norvig《Teach Yourself Programming in Ten Years》附录中的延迟数字表,以及 GitHub 上 gist "Latency Numbers Every Programmer Should Know"。这些不断被社区更新的延迟标尺表是估算系统理论性能下界的速查工具,建议背下关键数量级:内存 100ns、SSD 随机读 100μs、HDD 寻道 10ms、同机房 RTT 0.5ms、跨洲 RTT 150ms。
  • Colin Percival 的演讲与文章《The Linux Scheduler: a Decade of Wasted Cores》(2016)以及 Netflix Tech Blog 的性能专栏。Netflix 是本书方法论的重要实践场,其技术博客上有大量真实案例:如何在十万台服务器上部署 always-on 剖析、如何用 eBPF 定位 JVM 问题,是"性能工程在超大规模下如何组织"的一手资料。
1/13系统性能
0%