Linux 性能调优实战:CPU、内存与 I/O 瓶颈定位

服务器突然变慢、接口超时、load 飙升——绝大多数线上故障背后,都是 CPU、内存或磁盘 I/O 的某一项被打满。Linux 性能调优不是玄学,而是一套可复用的瓶颈定位流程:先建基线,再分层排查,最后用数据说话。本文用 top、vmstat、iostat、perf 等原生工具,带你从「系统很卡」快速定位到「到底是谁在吃资源」,每一步都给出可直接复制的命令与危险阈值。

一、先建基线:别一上来就 kill 进程

排查的第一原则:先看全局负载,再下钻到具体资源。很多人一看到慢就重启服务,结果故障反复。先用 uptime 看 1/5/15 分钟平均负载,它约等于「正在运行 + 等待 CPU 的进程数」。

$ uptime
 14:22:01 up 38 days,  3:11,  2 users,  load average: 1.05, 0.87, 0.64

# 经验线:load 长期 > 逻辑核数,说明 CPU 已饱和
$ nproc        # 查看逻辑核数,例如 8 核
$ lscpu | grep -E 'CPU\(s\)|Thread|Core'

一般 load 在核数的 70% 以内算健康;持续超过 1 倍说明有排队,超过 2 倍就要警惕响应劣化。注意:load 高不一定等于 CPU 满——也可能是 I/O 等待(下文第四节)或进程抢锁。

二、CPU 瓶颈定位:找到「最忙的那个进程」

进入 top 后按 P 按 CPU 使用率排序,按 1 展开多核。重点关注 %CPU 和 %wa(iowait,若长期 >20% 多为磁盘拖后腿,非 CPU 本身)。

# 按进程看 CPU,每秒刷新,只看前 10
$ top -b -d 1 -o %CPU | head -20

# 看某个进程的线程级开销(Java/Go 多协程场景常用)
$ pidstat -t -p 13254 1

# 找「热点函数」:采样 CPU 上的指令,无需重启进程
$ perf top -p 13254
$ perf record -F 99 -p 13254 -g -- sleep 30 && perf report

上下文切换过多也会「忙而无效」。用 vmstat 1 看 cs 列,若每秒几十万次且 us 不高,往往是锁竞争或频繁短连接;pidstat -w 可定位自愿/非自愿切换最高的进程。

熟悉 top 里的 CPU 行也很有用:us 是用户态占比,sy 是内核态(syscall 过多会偏高),id 空闲,wa 等待 I/O,st(steal)是被虚拟化层抢走的时间——在云服务器上 st 持续偏高,说明宿主机超卖,调应用无济于事,只能升配或迁规格。如果 sy 高而 us 低,优先怀疑频繁的系统调用(如大量小文件读写、短连接),而不是业务代码本身。

三、内存瓶颈定位:free 不够看,要看 available

新手常误读 free 列——Linux 会主动用空闲内存做页缓存(cache/buffer),所以 free 小不代表内存紧张。真正该看的是 available:内核评估后还能分给应用的内存量。

$ free -h
              total        used        free      shared  buff/cache   available
Mem:           15Gi       9.2Gi       1.1Gi      420Mi       5.3Gi       4.8Gi
Swap:         2.0Gi       0.0Ki       2.0Gi

# si/so 为换入换出,长期非 0 说明已在用 Swap,性能会骤降
$ vmstat 1
procs --memory-- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs  us sy id wa
 2  0      0  1.1G   410M  5.3G    0    0    12    88  320  980  18  6 75  1

若 available 持续走低、si/so 不为 0,下一步就是 OOM——容器内常见的是 cgroup 内存上限触发,表现为进程被直接杀死。这类问题我们在《OOMKilled 排查实战:容器内存限制与 JVM 堆外》里有完整复盘,建议结合本文一起看。

顺带提两个常被忽略的点。其一,页缓存是「借来的内存」:它会在应用需要时被自动回收,所以不要因为 free 小就急着加内存。若想验证「应用真实占用」,用 cat /proc/meminfo 看 MemAvailable,或按进程 smem -t -k 排序。其二,/proc/sys/vm/drop_caches 可手动清缓存做测试(echo 3 > /proc/sys/vm/drop_caches),但生产环境千万别拿它当「释放内存」的常规手段——那只是把缓存腾出来又马上被重新填,反而增加 I/O。

四、I/O 瓶颈定位:await 与 %util 是核心

磁盘慢是「隐形杀手」,它让 CPU 在 iowait 上空转。用 iostat -x 1 看扩展统计,关键两列:%util(设备繁忙度,长期接近 100% 即饱和)和 await(单次 I/O 平均等待毫秒,远高于 svctm 说明在排队)。

$ iostat -x 1
Device   r/s   w/s    rkB/s  wkB/s  await  aqu-sz  %util
sda     12.0  480    1500   22000   8.20   3.9     99.1

# 找「哪个进程在狂写磁盘」
$ iotop -o        # 只显示有 I/O 的进程
$ lsof -p $(pgrep mysql) | grep REG | head   # 看进程打开了哪些文件

磁盘空间也要随手确认:df -h 看挂载点用量,du -sh * 在可疑目录逐层定位大文件。inode 耗尽(文件数爆满)同样会让写入失败,可用 df -i 单独排查。

还有一个隐藏开关:脏页回写。Linux 会把写操作先攒在内存的「脏页」里,再批量落盘。若瞬间大量写入(如批量导入、日志刷盘),/proc/sys/vm/dirty_ratio 触顶会阻塞进程。用 cat /proc/meminfo | grep Dirty 看当前脏页量,sync 可强制落盘。顺序写被读拖慢时,多半是读请求和写请求抢同一块磁盘——此时上 SSD 或把读写分到不同盘,比调参数立竿见影。

五、速查表:三类瓶颈的一眼判定

资源首选命令关键指标危险阈值
CPUtop / pidstat / perf%CPU、csload > 核数、cs 每秒 > 20 万
内存free -h / vmstatavailable、si/soavailable<10%、so 持续非 0
I/Oiostat -x / iotop%util、await%util≈100%、await>15ms

六、真实案例:一次 load 飙到 30 的排查

某天凌晨告警,一台 8 核机器 load 从 2 涨到 30,API 大面积 504。排查顺序:top 看到 %wa 高达 60%,iostat -x 显示 sda %util=100%、await=120ms——确认是磁盘 I/O 饱和而非 CPU。进一步 iotop 锁定一个日志服务在每秒写入 800MB 且无轮转。

临时措施是限流日志写入并清理旧文件,load 5 分钟内回落到 1.2。这种「I/O 打满→上游等待→502/504 雪崩」的链路,与我们在《Nginx 502/504 排查实录:从超时到连接池》里描述的超时传导如出一辙,根因往往不在网关本身,而在后端资源。

七、把调优沉淀为持续监控

手动排查救得了一次火,救不了天天火。把上面的指标用 node_exporter 暴露,接入《Prometheus + Grafana 监控面板实战》里的采集体系,对 node_load1、node_memory_MemAvailable_bytes、node_disk_io_time_seconds 设阈值告警。这样瓶颈在影响用户前就被看见,调优也从「救火」变成「巡检」。

八、调优实战中的三个常见误区

误区一:load 高就加 CPU。 前面说过 load 高可能是 I/O 或锁,盲目升配只是把账单变厚。先分清 %wa 和 cs 谁在作怪,再决定加机器还是改代码。

误区二:free 小就是内存泄漏。 多数情况是页缓存正常工作。先盯 available 趋势和 si/so,再谈泄漏。真正的泄漏往往表现为 available 缓慢单调下降、几天内逼近 0。

误区三:一次 perf 就下定论。 采样 30 秒可能正好踩中 GC 或定时任务。至少在不同时段采样 2–3 次,对比热点是否稳定,避免被瞬时抖动误导。

九、小结

Linux 性能调优的套路其实很固定:uptime 看负载 → top/pidstat 锁 CPU → free/vmstat 看内存 → iostat/iotop 查 I/O → perf 追热点函数。记住三句话——load 高先分清是 CPU 还是 I/O;内存看 available 别看 free;磁盘看 %util 和 await。把这套命令练熟,线上 80% 的「系统很卡」都能在十分钟内定位到元凶。

上一篇 Tailwind CSS 实战:原子化 CSS 与工程化落地
下一篇 gRPC 实战:从 Proto 定义到四种通信模式落地
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]