服务器瓶颈如何定位?CPU、磁盘IO、网络与锁竞争综合分析

线上服务变慢,大家的第一反应往往是"是不是 CPU 不够了""加台机器吧"。但真正慢下来的原因千差万别:CPU 可能一点不忙,卡在磁盘 IO 上;磁盘也没问题,是网络在丢包;网络也正常,是应用里一把锁把所有线程堵住了。盲目加机器、换硬件,经常钱花了故障还在。

这篇文章给一套可操作的瓶颈定位方法论:先用 USE 方法建立排查框架,再按 CPU、内存、磁盘 IO、网络、锁竞争五个分层逐一分析,给出对应工具(top/vmstat/iostat/sar/perf/ss),重点讲锁竞争的思路,最后用一个从现象到结论的完整案例把流程串起来。文中数字为经验量级,工具用法以你的系统版本为准。

一、先立规矩:先测量,后优化

性能优化最大的忌讳是"猜"。凭直觉改配置、加硬件,改完不知道有没有效果,甚至引入新问题。正确顺序是:

  1. 测量:用数据说明现在慢在哪。
  2. 定位:找到真正的瓶颈项。
  3. 假设:提出一个可验证的优化假设。
  4. 验证:改一处,再测,用数据确认。
  5. 重复:瓶颈往往不止一个,解决一个会露出下一个。

一次只改一个变量,是性能优化的铁律。

USE 方法:给每个资源三个问题

排查任何资源,都问三件事:

  • Utilization(使用率):这个资源有多少时间在忙?低于 60% 通常不是瓶颈,长时间 > 80%~90% 要警惕。
  • Saturation(饱和度):有没有请求在排队等待?排队是瓶颈的强信号。
  • Errors(错误):有没有报错?错误本身也是瓶颈的表现。

对 CPU、内存、磁盘、网络分别套用,就不会漏。

二、分层定位:五个可能的瓶颈

层次 典型现象 关键指标 常用工具
CPU 加载慢、响应慢 us/sy/si 高、runq 长 top、vmstat、mpstat、perf
内存 频繁换页、OOM free、swap 使用、si/so free、vmstat、top
磁盘 IO 读写卡顿 await、%util、IOPS iostat、iotop
网络 延迟高、丢包、重传 带宽、重传、丢包 ss、sar、netstat、iperf3
锁竞争 多核并发上不去 sy 高、上下文切换高、CPU 不均 perf、vmstat、栈分析

关键思路:看"谁在等"和"谁在忙"。 真正的瓶颈,是那个"忙不过来、让别的资源都在等它"的资源。

三、工具速查与用法

先把常用工具和它们的"主看什么"记熟:

# 全局第一眼:负载、进程、CPU
top -bn1 | head -20

# CPU 细分 + 上下文切换 + 内存换页(每秒刷新)
vmstat 1

# 每核 CPU 使用率与软中断
mpstat -P ALL 1

# 磁盘 IO:await、%util、IOPS
iostat -x 1

# 系统活动历史/实时(需 sysstat)
sar -u 1 5      # CPU
sar -d 1 5      # 磁盘
sar -n DEV 1 5  # 网络

# 网络连接与状态
ss -s
ss -tan state established | wc -l

# 进程级 IO 占用(需安装 iotop)
sudo iotop -o

# CPU 火焰图/热点函数(需 perf)
sudo perf top
sudo perf record -g -p  -- sleep 30
sudo perf report

安装(部分工具需要):

# RHEL / CentOS / Rocky / AlmaLinux
sudo dnf install -y sysstat iotop perf
# CentOS 7
sudo yum install -y sysstat iotop perf

# Debian / Ubuntu
sudo apt update && sudo apt install -y sysstat iotop linux-tools-common linux-tools-generic

各工具的判读要点:

  • vmstat 的 r(运行队列) 长期大于 CPU 核数,说明 CPU 排队,CPU 是瓶颈。b 列阻塞进程多,往往和 IO 有关。
  • vmstat 的 wa(IO 等待百分比) 高,说明 CPU 在等磁盘。
  • vmstat 的 si/so 非零且持续,说明在换页,内存不够。
  • vmstat 的 cs(上下文切换) 异常高(如十万级/秒),结合 sy 高,多为锁竞争或线程频繁切换。
  • iostat 的 await 和 %util:await 升高、%util 接近 100%,磁盘是瓶颈。
  • mpstat 的 %soft:软中断高,通常是网络流量大或网卡中断处理吃满单核。
  • %sys 高、%usr 低:多在系统调用、锁、上下文切换上,而非业务计算。

四、锁竞争的分析思路

锁竞争是最"隐蔽"的瓶颈:CPU 看着很忙(%sys 高),但业务吞吐上不去,加核也没用。典型特征:

  • 多核没有扩展性:核数翻倍,QPS 不涨甚至下降。
  • %sys 和上下文切换都高:线程在不停争锁、挂起、唤醒。
  • CPU 使用不均:某个核被锁持有者或调度器占用,其他核在等。

分析步骤:

  1. 确认并发扩展性:分别用 1、2、4 核压测同一服务,看 QPS 是否随核数上升。若不涨,怀疑锁。
  2. 看系统指标:vmstat 的 cs 高、sy 高,r 列(运行队列)可能有较多线程争抢。
  3. 抓火焰图找热点:用 perf 抓取,看栈里是否大量停在锁相关的函数(如 futex、mutex、自旋锁)。
# 采集 30 秒火焰图数据,聚焦该系统调用/锁相关热点
sudo perf record -g -F 99 -p  -- sleep 30
sudo perf report --stdio | head -50
# 关注是否有大量时间花在 futex、__lll_lock 等锁相关符号上
  1. 看应用层:线程池是否过小、是否有全局锁、是否共享可变状态、连接池是否够用。很多"锁竞争"其实来自一把粗粒度的全局锁或串行的单点(如单一数据库连接、单例对象)。

  2. 优化方向:缩小锁粒度、用无锁结构、分片、减少共享、延长连接池、把串行点并行化。改完必须复测,确认 sy 和上下文切换下降、QPS 上升。

五、从现象到瓶颈的定位路径

把上面的东西串成一条可执行路径:

  1. 看全局:top/vmstat,判断是 CPU、内存、IO 还是都正常。
  2. 看是不是 CPU:r 列 > 核数、%usr 高 → CPU 密集;%sys/%soft 高 → 系统调用/锁/网络。
  3. 看是不是内存:si/so 持续非零、swap 在用 → 内存不足换页。
  4. 看是不是磁盘:wa 高、iostat 的 await/%util 高 → 磁盘瓶颈,再用 iotop 定位是哪个进程。
  5. 看是不是网络:%soft 高、ss 里连接状态异常、丢包/重传 → 网络瓶颈,用 iperf3、mtr 排查。
  6. 都不明显但慢:怀疑锁竞争、下游依赖、GC、或压测机自身瓶颈,进入更细的分析(perf、应用日志、下游耗时)。

口诀:先看资源、再看等待、最后看应用。

六、一个完整案例

现象:某接口 QPS 从 3000 掉到 1200,用户抱怨变慢,CPU 使用率却只有 40%。

排查过程:

  1. top:发现 CPU 只用了 40%,但 %sys 占了 30%,%usr 只有 10%,跟"业务计算"不符。
  2. vmstat 1:cs(上下文切换)高达十几万/秒,r 列长期大于核数,说明线程在频繁争抢。
  3. 怀疑锁竞争:做并发扩展测试,1 核 QPS 1000、2 核 1100、4 核 1200——几乎不涨,强烈提示锁。
  4. perf record -g:火焰图显示大量时间停在锁等待相关的系统调用上。
  5. 应用层定位:找到一处全局配置对象被高频读写,用一把全局互斥锁保护,所有请求串行经过。
  6. 优化并复测:改成读写锁 + 局部缓存,cs 降到合理水平,QPS 从 1200 恢复到 3500,%sys 从 30% 降到 8%。

结论:CPU 不高不代表没瓶颈,得看 CPU 花在哪。%sys 和上下文切换这两个指标,是锁竞争的关键线索。

七、把定位经验沉淀成监控

一次定位解决的是当下,把关键指标沉淀成长期监控,才能提前发现下一个瓶颈。建议把这些指标接进监控告警:

  • 资源层:CPU 各维度利用率、内存与 swap、磁盘 await 与 %util、网络带宽与丢包、上下文切换。
  • 业务层:QPS、P95/P99 响应时间、错误率、连接数、GC 停顿。
  • 关联分析:把业务指标和资源指标放在同一个时间轴上,一旦 QPS 掉、错误涨,能立刻看出是哪一层先动。

监控的价值在于"看见趋势"。等到用户报障才发现,通常已经晚了;指标持续爬升时就该介入,这也是把性能工作从"救火"变成"预防"的关键。

常见问题

Q1:CPU 使用率不高,是不是就没瓶颈?
不是。CPU 不高但慢,瓶颈可能在磁盘、网络、锁竞争、下游依赖或 GC。要看 CPU 的细分(us/sy/si/wa)和等待指标,而不是只看总使用率。

Q2:先优化哪个资源?
先找"饱和并有排队"的那个。用 USE 方法对每个资源看使用率、饱和度和错误,瓶颈通常就是"最忙且有人排队"的那一项。

Q3:vmstat 的 wa 高就一定是磁盘问题吗?
不一定,网络 IO、NFS 挂载卡住也会体现为等待。结合 iostat 的 await/%util 和 iotop 确认到底是哪块设备或哪类 IO。

Q4:上下文切换高怎么办?
先确认是锁竞争还是线程数过多。减少共享、缩小锁粒度、合理设置线程池、避免惊群。然后用 perf 找热点函数。

Q5:定位完一个瓶颈,优化后需要重新定位吗?
需要。解决一个瓶颈后,压力会转移到下一个最弱的环节,可能露出新的瓶颈。优化是迭代过程,要反复测量。

总结

  • 性能问题先测量再优化,一次只改一个变量,用 USE 方法(使用率、饱和度、错误)对每个资源排查。
  • 分五层看:CPU 看 us/sy/si 和 runq,内存看换页,磁盘看 await/%util,网络看丢包/重传/软中断,锁竞争看 %sys 和上下文切换。
  • 工具速查:top/vmstat/mpstat/iostat/sar/ss/iotop/perf,先看资源、再看等待、最后看应用。
  • 锁竞争的标志是"多核无扩展性 + %sys 高 + 上下文切换高 + CPU 使用不均",用并发扩展测试和 perf 火焰图定位。
  • 方案要落到可验证:改完必须复测,确认指标改善;解决一个瓶颈后要重新定位下一个。