性能指标与度量:CPU、内存、磁盘、网络、吞吐量、延迟
性能调优这件事,说白了就是跟数字打交道。我刚入行的时候,总觉得调优就是凭感觉改参数,结果经常把系统改崩了。后来才明白——没有度量,就没有优化。你连瓶颈在哪都不知道,怎么下手?
这节课我们就把性能调优最核心的几类指标过一遍。CPU、内存、磁盘、网络,再加上吞吐量和延迟这两个综合指标。我保证,搞懂这些,你再看系统性能问题,心里就有底了。
CPU 指标:算力到底够不够?
CPU 是系统的发动机。我见过太多人一看到 CPU 100% 就慌了,其实不一定有问题——如果你跑的是 CPU 密集型的计算任务,100% 反而是好事,说明资源用满了。真正该警惕的是CPU 满载但吞吐量上不去,那说明有锁竞争或者上下文切换过多。
关键指标就这几个:
- %user:用户态CPU占用高?说明应用在干活,合理。
- %sys:内核态CPU占用高?小心系统调用频繁,可能是I/O或锁问题。
- %iowait:CPU在等磁盘/网络?这是性能瓶颈的常见信号。
- %idle:空闲太多?资源可能没喂饱,或者架构有瓶颈。
- 上下文切换次数 (cswch/s):这个值如果大于10万/秒,通常意味着线程数过多或锁粒度太细。
我在项目中遇到过一个问题:一个 Java 服务 CPU 一直在 90% 左右,但每秒请求数只有几百。top 一看,%sys 占了 30%。再用 strace 跟一下,发现是日志框架每写一条日志就做一次文件锁。换成异步日志后,CPU 降到 40%,吞吐量翻了 3 倍。
# 用 top 看 CPU 分解
top -bn1 | grep "Cpu(s)"
# 结果类似:
# %Cpu(s): 45.2 us, 12.8 sy, 0.0 ni, 38.7 id, 3.3 wa, 0.0 hi, 0.0 si, 0.0 st
# 用 vmstat 看上下文切换
vmstat 1 3
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
# r b swpd free buff cache si so bi bo in cs us sy id wa st
# 3 0 0 2048000 5000 200000 0 0 10 20 1000 80000 45 13 39 3 0
内存指标:你的应用到底吃了多少?
内存调优的核心不是看还剩多少,而是看谁在用、怎么用的。我习惯先用 free -m 看个大概,再用 /proc/meminfo 细看。当然,对于 Java 应用,还得上 jmap/jstat 看堆内存。
| 指标 | 含义 | 警戒值 |
|---|---|---|
| MemTotal | 物理内存总量 | — |
| MemFree | 空闲物理内存 | 低于10%时注意 |
| Buffers / Cached | 系统缓存(包括磁盘缓存、inode缓存等) | 不必担心,系统会自动释放 |
| SwapUsed | 用了多少交换分区 | 大于0就说明物理内存不够用了 |
| Page Faults (majflt) | 主缺页中断(需要从磁盘读数据到内存) | 持续>100次/秒就是瓶颈 |
我遇到过一个经典案例:一个 Redis 缓存集群,明明有 64G 内存,用 top 看到可用内存还剩 40G,但响应越来越慢。我说不对,肯定有什么猫腻。查了 /proc/meminfo,发现 Slab 占了 20G——原来是内核的 dentry cache(目录项缓存)炸了。文件数量太多,dentry 缓存一直在增长,导致内存碎片化。最后调了 vm.vfs_cache_pressure=200 才解决。
# 查看 Slab 内存占用
cat /proc/meminfo | grep -E "Slab|SReclaimable|SUnreclaim"
# 调整 dentry 缓存回收压力
sysctl -w vm.vfs_cache_pressure=200
磁盘 I/O 指标:系统到底在读写什么?
磁盘往往是最大的瓶颈,尤其是机械盘。现在 SSD 好一些,但 I/O 延迟依然是关键。我个人习惯用 iostat -x 1 盯几个核心指标:
- r/s + w/s:每秒读写请求数。单个机械盘一般能到 200~300,SSD 能到 10万+。
- rkB/s + wkB/s:吞吐量。结合队列深度看。
- await:平均每次 I/O 的等待时间(包含排队+服务)。机械盘一般 5~10ms,SSD 在 0.1~1ms。超过 20ms 就慢得离谱了。
- %util:磁盘繁忙百分比。100% 说明完全饱和,但不一定就是瓶颈——队列深度也很重要。
- svctm:实际服务时间(不包括排队)。如果 svctm 接近 await,说明排队少,瓶颈在磁盘本身;如果 await 远大于 svctm,说明排队严重。
我曾经优化过一个日志收集系统。写日志的日志盘 %util 才 30%,但 await 已经 50ms 了。一看 svctm,才 2ms。说明磁盘服务能力足够,但请求排队太长——因为日志进程用了同步写,每个请求都在等 fsync。换成批量写+异步 fsync,await 降到 5ms,吞吐提高了 5 倍。
# 用 iostat 看磁盘延迟
iostat -x 1 3
# 输出示例(关键列):
# Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
# sda 0.00 10.00 100.00 200.00 500.00 1000.00 10.00 2.50 8.33 2.00 60.00
网络指标:带宽延迟与丢包
网络调优很容易被忽视。我记得有一次线上告警,用户反馈下单很慢,但后端服务器负载都很低。查了网络才发现,延迟高达 500ms——原来是内网交换机的一个端口协商到了百兆(正常应该千兆)。
网络指标分两大类:
- 延迟类:ping 测网络延迟,TCP 连接建立时间(通过 tcpdump 分析握手包),服务端处理时间(应用日志记录)。
- 吞吐量类:带宽使用率(iftop/nethogs 看),每秒数据包数(pps),丢包率(netstat -i 里的 dropped 列)。
我常用这些命令:
# 看网卡整体统计
netstat -i
# Kernel Interface table
# Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
# eth0 1500 12345678 0 12 0 9876543 0 4 0 BMRU
# 看实时带宽
iftop -i eth0 -n -P
# 看 TCP 连接状态
ss -tan | awk '{print $1}' | sort | uniq -c
# 输出示例:
# 10 ESTAB
# 3 TIME-WAIT
# 2 CLOSE-WAIT
这里有个点要注意:TIME-WAIT 太多不一定是坏事,它只是表示连接正常关闭后的等待状态。但 CLOSE-WAIT 太多就危险了,说明应用没有正确关闭连接。我曾经调过一个 Web 服务,CLOSE-WAIT 堆积到几万个,最后后端连接池用尽。排查后发现是 HTTP 客户端没有设置超时,修复后一切正常。
综合指标:吞吐量与延迟
吞吐量(Throughput)和延迟(Latency)是性能优化的终极目标。这两个指标通常是跷跷板关系——你想提高吞吐,往往就要容忍更长的排队延迟;你想降低延迟,可能就要牺牲吞吐。
我习惯用队列模型来理解:
- 吞吐量 = 处理速率 × 并行度
- 延迟 = 服务时间 + 排队时间
现实中,你应该关注尾延迟(比如 P99 延迟),而不是平均值。因为 99% 的请求如果都很快,但 1% 的请求慢到超时,用户体验依然很差。我在电商大促时调过 Redis 缓存,平均延迟 2ms,但 P99 延迟达到了 200ms。后来发现是热 Key 导致单节点过载,用本地缓存分流后才降到 5ms。
一张图看懂指标关系
下面我画了一张关系图,帮你理清这些指标是怎么串起来的。你可以把它当作调优的思维框架。
从这张图你可以看出:用户请求进来,先看吞吐量和延迟是否达标。如果不达标,就往下一层看——是CPU满了?内存不够了?磁盘卡住了?还是网络带宽瓶颈?逐层排查,才能找到真凶。
一个小结
你想想看,性能调优就像医生看病。CPU、内存、磁盘、网络就是四大生命体征,吞吐量和延迟就是你感受到的症状。没有指标,你连病人的体温都不知道,怎么开药?
我个人习惯在服务启动时就收集好这些指标,做成监控大盘。不要等出问题了再临时抱佛脚。下一节我们会讲具体的监控工具和可视化方法,但那之前,你得先把这些指标的含义刻在脑子里。
嗯,这章内容不少,但都是干货。你先消化一下,有什么问题欢迎在评论区留言。我当年啃这些指标的时候也花了很长时间,别急,一步步来。