参考来源与版权说明
这章解决什么问题
资源指标能告诉你应用消耗了什么,却未必说明应用为何做这些工作。应用层离业务操作最近,往往拥有最大的优化空间:消除无用请求、选择更合适的算法、减少小 I/O、改善缓存、调整并发模型,通常比在内核末端微调收益更高。本章建立“业务操作 → 线程状态 → 代码路径 → 系统资源”的分析链。
开始前要回答应用做什么、服务哪些请求、SLO 是什么、如何配置、运行在哪种主机/配额内、有哪些指标与日志、版本和已知问题是什么。没有目标,CPU 降低 10% 也无法判断是否值得;有目标,才能选择优化延时、吞吐量、资源效率还是成本。
五条核心结论
- 从业务操作和目标开始。 请求类型、速率、延时分布和错误比“进程有点忙”更可行动。
- 先分 on-CPU 与 off-CPU。 前者通过 CPU 剖析解释,后者再分为调度、I/O、锁、睡眠和等待工作。
- 优化常见路径。 火焰图最宽的代码路径、最高频系统调用和主要等待状态,比随机阅读源代码更值得先看。
- 并发不等于并行。 多线程可隐藏 I/O 等待;真正同时使用多颗 CPU 还需并行,并会引入同步与调度成本。
- 可观测性本身是长期性能能力。 有清晰指标、符号和栈的应用,更容易发现并消除无效工作。
核心模型与关键指标
一次请求的墙钟时间
├─ on-CPU
│ ├─ 用户态:算法、解析、压缩、GC、运行时
│ └─ 内核态:系统调用、中断相关路径
└─ off-CPU
├─ 等待 CPU(可运行)
├─ 磁盘/网络/缺页
├─ 锁与条件变量
├─ 主动睡眠
└─ 空闲等待下一项工作关键指标包括请求率、错误率、延时的中位数与尾部;进程/线程用户和系统 CPU;可运行队列延时;I/O 尺寸、方向、次数与持续时间;系统调用类型和速率;锁竞争与持锁时间;线程池繁忙比例、排队长度、拒绝数;缓存命中率、GC 暂停、文件描述符与连接池使用量。
应用技术存在权衡:较大 I/O 能摊薄固定开销,却可能增加不需要的数据和单次延时;缓冲写提高吞吐量,却会增加等待;轮询可降低事件到达后的响应时间,却消耗 CPU;更多线程能增加并行度,也可能扩大上下文切换、缓存争用与锁竞争。
诊断步骤
- 选择一个具体操作和 SLO,按请求类型记录速率、错误与延时分布,确认工作负载是否改变。
- 检查线程池、连接池、队列、缓存和资源限制,识别饱和、拒绝、降级模式与异常配置。
- 将处理请求的线程时间分为用户、内核、可运行、换页、磁盘、网络、睡眠、锁和空闲九类;先确定最大非空闲状态。
- on-CPU 高时,采样用户+内核栈并看火焰图最宽的塔;调查高层调用者与顶部直接执行函数。
- off-CPU 高时,仅关注处理请求的线程或函数,排除正常等待工作的线程,再判断 I/O、锁还是调度延时。
- 内核时间或 I/O 可疑时,统计系统调用类型、次数、参数与持续时间;高频小 I/O、重试或短命进程常是直接线索。
- 分布式请求用 trace/span 找到占主要时间或报错的服务,再回到该服务执行上述单机分析。
- 修改后按相同请求组合复验延时、吞吐量和资源成本,防止用牺牲另一个目标换来局部改善。
常用工具与命令
pidstat -p PID 1
pidstat -d -p PID 1
ps -L -p PID -o pid,tid,psr,stat,pcpu,comm
perf top -p PID
perf record -F 99 -g -p PID -- sleep 10
profile -F 49 -p PID 10
offcputime -p PID 5
strace -c -p PID将 PID 替换为目标进程。前几项是观察型工具,但 perf top、采样和 BPF 工具仍有开销;profile、offcputime 是否存在取决于 BCC/BPF 安装与权限。strace 通过 ptrace 跟踪系统调用,在高系统调用率进程上可能造成巨大减速,只应在测试环境或明确评估风险后短时、限范围使用。选项随版本不同,以本机帮助为准。
如何解读结果
pidstat的%usr高,优先剖析应用/运行时代码;%system高,继续看系统调用、缺页、中断或内核路径。- 火焰图宽度代表样本占比,不是函数单次耗时或调用次数。先看宽塔,再沿下方祖先理解“谁触发了它”。
- off-CPU 火焰图往往被线程池正常空闲淹没。必须过滤到请求处理函数、目标 PID/线程或合理的等待时长。
iodelay、D状态或文件系统函数只是方向;要证明业务影响,还需确认该请求同步等待这些事件。- 系统调用汇总中大量
read/write要结合每次字节数;大量短调用可能意味着缓冲不足,也可能是协议本身需要。 [unknown]或只有一两层的栈通常是符号/栈展开问题,不代表代码没有调用者。可用 debuginfo、保留帧指针或运行时专用剖析器修复可见性。
常见误判
- 认为线程越多吞吐量越高。 锁、CPU 数、缓存和调度开销会让收益下降甚至反转。
- 把所有 off-CPU 都算成性能损失。 服务线程等待下一请求属于正常空闲,不能作为优化目标。
- 只看用户态剖析器。 它可能看不到内核时间和被取消调度的时间,造成墙钟时间解释不完整。
- 把大 I/O 当通用优化。 随机小读若被放大,会浪费带宽、缓存和延时。
- 用 `strace` 得到慢结果后怪应用。 观测器自身可能改变系统调用时序,应先量化开销。
- 看到缺失符号就猜函数。 先修复符号和栈,再作代码级结论。
五分钟自测
- 为一个 Web 接口写出延时、吞吐量和资源效率三个不同目标。
- 线程处于可运行、磁盘等待、锁等待时,下一步各看什么证据?
- CPU 火焰图中宽框与高框分别表示什么?
- 为什么完整 off-CPU 火焰图常被“正常等待”主导?
- 系统调用次数很多时,为什么还必须看尺寸、持续时间和调用栈?
本章小结
应用性能分析要把资源消耗重新关联到业务请求。先确定目标和工作负载,再以线程状态把时间分到 on-CPU 与各种 off-CPU 原因;CPU 剖析、系统调用和等待栈随后解释具体代码路径。并发、缓存、缓冲和 I/O 大小都有收益与代价,任何优化都要回到同一 SLO、同一负载组合复验。
来源定位
| 主题 | PDF 页码 |
|---|---|
| 应用上下文、目标、SLO 与可观测性 | 224-228 |
| 算法复杂度、I/O、缓存和缓冲 | 228-230 |
| 轮询、并发、并行、同步和非阻塞 I/O | 230-236 |
| 语言、运行时、JIT 与垃圾回收 | 237-240 |
| CPU/off-CPU、系统调用与 USE | 240-247 |
| 九种线程状态、锁和静态配置 | 247-253 |
| perf、profile、offcputime、strace 与 BPF 工具 | 253-268 |
| 缺失符号和栈展开问题 | 268-271 |
| 练习与参考资料 | 271-273 |
章节测验
共 5 道单选题。答错有解析,答对看来源页码。