首页
学习
活动
专区
圈层
工具
发布
36 篇文章
1
Linux性能工具图谱
2
不会用这个工具,你的 Linux 服务器就是个摆设!
3
性能测试流程指南和工具推荐​
4
MySQL 导致 CPU 消耗过大,如何优化
5
测试团队绩效考核的关键指标
6
为什么要做稳定性保障?
7
性能测试-基础篇
8
jmeter使用心得(三)
9
性能测试
10
性能测试 —— MySQL 基准测试
11
MySql性能测试
12
测开必备:10大主流性能测试工具推荐
13
Linux应用性能分析及故障排查
14
性能测试之常见术语浅析
15
性能测试面试问答
16
软件测试|性能测试中常用的性能指标有哪些?
17
大话性能测试系列(3)- 常用的性能指标
18
性能专题:一文搞懂性能测试常见指标
19
一文搞懂,性能测试指标评估方法
20
性能监控与压力测试
21
【性能测试】性能需求挖掘、性能方案制定及压测场景设计之疑惑与思考(一)
22
软件性能测试方案-性能测试准备
23
【软件测试】性能测试
24
软件测试之性能测试
25
从代码到设计的性能优化指南
26
性能优化:量变引起质变的挑战
27
TPS、QPS、吞吐量、并发用户数区别及理解(二)
28
压力测试指标(QPS、TPS、PV、RT)
29
Locust性能测试实战
30
玩转Jmeter进行性能测试
31
JMeter接口性能测试从入门到精通
32
Linux 系统性能调优:CPU、内存、IO 瓶颈分析方法(草履虫版)
33
运维自动化脚本集锦:那些能救命的 Bash 与 Python 小工具
34
性能工具之Taurus场景使用(进阶篇)
35
性能测试工具--Locust官方文档(API)解读(全)
36
性能测试之k6篇

Linux 系统性能调优:CPU、内存、IO 瓶颈分析方法(草履虫版)

0. 写在前面:为什么你需要“神器”而非“常用命令

把系统从「慢」变成「稳」有时候不需要大折腾,而是把最显著的瓶颈找出来。下面是一套我常用的方法.


先说一句话的心法:先把用户感受恢复,再去追根溯源。很多时候临时的减害手段(降级、限流、回滚、切流)比立刻找根因更重要。下面分三类瓶颈:CPU、内存、IO(含磁盘与网络),每类给出诊断命令、常见判别依据、应急缓解和长期修复建议。


常用工具速览(先熟悉这些工具)

这些命令几乎是现场救火的工具箱,建议熟练掌握:

代码语言:javascript
复制
top, htop, vmstat, mpstat, pidstat, ps, perf, strace, pstack
free, slabtop, smem
iostat, iotop, blktrace, lsblk, df, du, smartctl
ss, netstat, iftop, iperf3, tc
sar (sysstat), dstat, atop
tcpdump (小心会产生日志)

我常把一台机器的检查脚本写成几行命令串,先快速跑一遍,得出第一轮结论,再深入。


一:CPU 瓶颈 — 如何判断与处理

诊断思路

看整体负载(load)只是起点,关键是区分:是用户态占满、内核态占满、还是等待 I/O(iowait)、或是被虚拟化的 steal(被抢占),再决定对症下药。

现场诊断命令(顺序执行,快速采样)

代码语言:javascript
复制
# 1) 快速看系统负载与总体 CPU 使用
$ top -b -n1 | head -n5
top - 11:30:00 up 12 days,  2:01,  1 user,  load average: 12.34, 11.98, 10.12
%Cpu(s): 92.0 us,  3.0 sy,  0.0 ni,  2.0 id,  3.0 wa

# 2) 更细:每个 CPU 的使用(查看是否存在核不均衡)
$ mpstat -P ALL 1 1
Linux 5.4.0 (host)  08/10/2025  _x86_64_  (8 CPU)
08:30:01 AM  CPU  %usr  %nice  %sys  %iowait  %irq  %soft  %steal  %guest  %idle
08:30:01 AM  all   92.0   0.00   3.0    3.0      0.0   0.0     0.0      0.0    2.0
08:30:01 AM    0   99.0    0.0   0.0    0.0      0.0   0.0     0.0      0.0    1.0
08:30:01 AM    1   85.0    0.0   6.0    0.0      0.0   0.0     0.0      0.0    9.0
...

# 3) 找出占用最严重的进程
$ ps aux --sort=-%cpu | head -n 8
root  23456  95.0  1.2 123456 78900 ?  R  11:29   120:00 /usr/bin/myapp --worker

判别

  • %usr 高,且某个进程占比高 → 进程计算密集(业务逻辑/算法);
  • %sys 高 → 内核开销高(例如频繁上下文切换、网络/磁盘中断);
  • %iowait 高 → 可能是 IO 瓶颈(继续看 IO 部分);
  • %steal 高 → 虚拟化宿主被抢占(云主机需联系云厂商或扩容)。

应急缓解(先恢复服务可用性)

  1. 1. 临时降级或限流:在流量入口(Nginx、LB)做限流或返回降级页面,果断减少负载。
  2. 2. 迁移/扩容:把部分实例从流量池摘除、扩容实例数或增加容器副本(Kubernetes):
代码语言:javascript
复制
# k8s 临时扩副本
$ kubectl scale deployment myapp --replicas=10 -n prod
deployment.apps/myapp scaled
  1. 3. 进程优先级调整/隔离:对非关键进程 renice 或把关键进程绑定到空闲核 taskset
代码语言:javascript
复制
# 优先级调低(nice 值增大)
$ sudo renice +10 -p 23456
# 把进程绑定到 CPU 2 和 3
$ sudo taskset -cp 2,3 23456

示例输出(模拟):

代码语言:javascript
复制
$ sudo renice +10 -p 23456
23456 (process id) old priority 0, new priority 10

深入修复(事后优化)

  • • 对热点函数进行剖面分析(perf record / perf report / FlameGraph),找到热点后优化算法或改为异步处理;
  • • 如果是大量系统调用/上下文切换,检查线程模型、减少锁争用或使用更合适的数据结构;
  • • 用 CPU 亲和性(cpuset)把关键服务放到独立核,避免干扰;
  • • 对延迟敏感服务,使用实时调度(谨慎)或内核参数微调。

perf 快速示例(采样 10s)

代码语言:javascript
复制
# 需要 root,示例模拟命令
$ sudo perf record -F 99 -p 23456 -g -- sleep 10
$ sudo perf report --stdio | head -n 20
# 输出会显示最耗时的函数栈,便于定位

二:内存瓶颈 — 如何判断与处理

诊断思路

内存问题分两类:一是内存不足导致频繁 swap(影响响应),二是内存泄露使某服务占用持续增长。判断依据:freevmstatslabtopps

现场诊断命令

代码语言:javascript
复制
# 1) 总览内存状态
$ free -m
              total        used        free      shared  buff/cache   available
Mem:          32768       25000        1024         123        5744        7000
Swap:         8192         512        7680

# 2) 观察短期页活动
$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 4  0  524288  102400  52480  300000  10   0  1024   128 3000  1000 60 30  5  5 0
...

# 3) 哪个进程占内存
$ ps aux --sort=-rss | head -n 10
appuser  34567  0.1 45.0  1234567 1500000 ? Sl  11:00  300:12 /usr/bin/myapp

# 4) slab 缓存检查(内核对象)
$ sudo slabtop -o
 Active / Total Objects:  123456 / 234567
  OBJS ACTIVE  USE OBJ SIZE  SLABS SIZE  CACHE NAME
  10240   5120   50%     64     80K     8192 kmalloc-64
...

判别

  • available 很低且 swpd 不为 0 → 发生交换(swap);
  • • 单个进程长期增长 → 内存泄露或长时间缓存;
  • • slabtop 显示某一类对象异常多 → 内核层面问题(例如大量 socket、inode 未释放)。

紧急缓解(非破坏性优先)

  1. 1. 短期释放 pagecache(谨慎,仅在确定为缓存占用且内存紧张时):
代码语言:javascript
复制
# 释放 pagecache
$ sudo sync; echo 1 | sudo tee /proc/sys/vm/drop_caches
1

小心:这会清空文件系统缓存,会影响 IO 性能,建议在低峰或先预警。

  1. 2. 调整 swappiness 临时时间窗口降低 swap 倾向:
代码语言:javascript
复制
$ sudo sysctl vm.swappiness=10
vm.swappiness = 10
  1. 3. 重启或重建进程:对泄露严重的服务先重启(注意要有 graceful 下线流程或在负载均衡中下线实例)。
  2. 4. 通过 OOM 策略保护关键进程:调整 oom_score_adj,防止关键进程被 OOM 杀死:
代码语言:javascript
复制
# 给 critical pid 设置 oom_score_adj 为 -1000
$ echo -1000 | sudo tee /proc/34567/oom_score_adj

示例输出(模拟):

代码语言:javascript
复制
$ sudo sysctl vm.swappiness=10
vm.swappiness = 10

深入定位(内存泄露诊断)

  • • 对 C/C++ 服务用 gdb/valgrind/heaptrack 做内存跟踪;
  • • 对 Java 用 jmap / jstack / heap dump 分析;
  • • 对 Go 服务用 pprof(net/http/pprof)做 heap profile。

Go pprof 示例如下(采样与生成报告)

代码语言:javascript
复制
# 在服务机器上采集 30s heap
$ curl http://127.0.0.1:6060/debug/pprof/heap > heap.out
# 使用 go tool pprof 进行分析
$ go tool pprof -svg ./myapp heap.out > heap.svg

三:磁盘 / IO 瓶颈 — 判断与解决

诊断思路

磁盘瓶颈通常表现为高 await、高 %util(iostat),或大量等待导致 CPU 的 iowait 上升。需要区分是单块盘饱和、文件系统元数据锁竞争,还是网络存储(NFS、iSCSI)问题。

常用诊断命令

代码语言:javascript
复制
# 1) 磁盘总体 IO 性能(查看 %util、await)
$ iostat -x 1 3
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm  %util
sda    0.00  0.00   0  100 0      102400  2048      12.34     45.67  0.00   45.67   2.00   95.00
# %util 近 100% 且 await 高 -> 磁盘成为瓶颈

# 2) 实时查看哪个进程在做最多 IO
$ sudo iotop -oPa --iter=3
Total DISK READ :       0.00 B/s | Total DISK WRITE : 512.00 K/s
PID PRIO USER     DISK READ  DISK WRITE  SWAPIN     IO> COMMAND
1234 be/4 appuser   0.00 B/s  512.00 K/s  0.00 %     0.00 % /usr/bin/myapp

# 3) 文件系统使用情况
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   45G  3.0G  94% /

# 4) 块队列长度(avgqu-sz)结合 %util 判断是否饱和
$ cat /sys/block/sda/stat
# 11个字段,通常看 avg queue depth 需用 iostat 更直观

判别

  • %util 高(> 80%)且 avgqu-sz 大 → 磁盘饱和;
  • await 高而 svctm 低 → 可能是队列过长或设备等待服务;
  • • iotop 指向某进程大量写入 → 是应用导致,还是日志/临时文件导致。

应急缓解

  1. 1. 减小写入压力:把日志等级调低、关闭不必要的持久化任务、临时把某些批处理调度到非高峰。
  2. 2. 迁移热点 IO:将热点目录迁移到另一块盘或更快的介质(NVMe、SSD),在文件系统允许下用 rsync 热迁移:
代码语言:javascript
复制
# 假设 /data 是热点目录,要迁移到 /mnt/fastdisk
$ rsync -aHAXv /data/ /mnt/fastdisk/data/
$ mount --bind /mnt/fastdisk/data /data
  1. 3. 调整 IO 调度器(对延迟敏感应用,选择 deadline 或 noop):
代码语言:javascript
复制
# 查看当前调度器
$ cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

# 切换为 deadline
$ echo deadline | sudo tee /sys/block/sda/queue/scheduler
deadline

模拟输出:

代码语言:javascript
复制
$ echo deadline | sudo tee /sys/block/sda/queue/scheduler
deadline
  1. 4. 临时开启更多副本或读写分离:如果是数据库写满盘,优先把写请求限制并把读请求迁移到只读副本。

深入修复(长期)

  • • 升级到更快存储,使用 RAID 0/10 或 NVMe,按成本与可靠性权衡;
  • • 对数据库做分库分表、写入合并或异步化,减少同步写;
  • • 在文件系统层面调优 mount 选项,例如 noatime、合理调整 journaling(ext4 的 commit 值);
  • • 对 SSD 做定期 TRIM:fstrim -v /

四:网络 IO 瓶颈 — 诊断与缓解

网络问题会表现为延迟、丢包或吞吐率受限。检查顺序:链路(链路丢包/延迟)→ 本机网络栈(SOCKET 队列、tc 策略)→ 应用层(连接泄露)。

命令与示例

代码语言:javascript
复制
# 基本连通/延迟
$ ping -c 4 10.0.0.10
PING 10.0.0.10 (10.0.0.10): 56 data bytes
64 bytes from 10.0.0.10: icmp_seq=1 ttl=64 time=1.23 ms
...
# TCP 层面连接问题
$ ss -s
Total: 123 (kernel 456)
TCP:   789 (estab 100, closed 200, orphaned 0, timewait 50)
# 查看端口连接详情
$ ss -tn state established '( dport = :3306 or sport = :3306 )'

缓解

  • • 对客户端连接数量暴增,使用 tc/iptables 做限速或连接数限制;
  • • 检查并增大 socket 缓冲区:sysctl -w net.core.rmem_max=...
  • • 若网络链路受限,联系网络团队或按需做流量剖分、QoS。

五:容器 / 虚拟化层面注意点

容器环境下,很多性能问题来源于资源配额不当或 cgroup 限制:

  • • 检查容器的 CPU/Memory requests & limits(Kubernetes):
代码语言:javascript
复制
$ kubectl describe pod mypod -n prod
  • • 容器被 OOMKilled 要看事件和 kubectl logs --previous
  • • 虚拟化(云)出现 %steal 高,请联系云厂商或考虑换实例规格。

六:快速排查清单(现场小抄)

在现场我常用的一套顺序(在 1–3 分钟内跑完):

代码语言:javascript
复制
# 系统总体
top -b -n1 | head -n20
free -m
vmstat 1 5
iostat -x 1 3
df -h

# 进程级别
ps aux --sort=-%cpu | head
ps aux --sort=-%mem | head

# 网络
ss -s
ss -tn state established

# 容器 / k8s
kubectl get pods -A
kubectl top nodes

把这些输出截图/保存到事件工单中,便于后续复盘与分析。


七:现场快速缓解模板(可复制)

1)对 CPU 峰值,临时扩容(Kubernetes)

代码语言:javascript
复制
kubectl scale deployment myapp --replicas=10 -n prod

2)对内存泄露,优雅下线并重启单实例

代码语言:javascript
复制
kubectl drain node/node-1 --ignore-daemonsets --delete-local-data
systemctl restart myapp.service

3)对磁盘写满,归档并释放日志

代码语言:javascript
复制
sudo mv /var/log/app.log /var/log/app.log.$(date +%F-%H%M)
sudo gzip /var/log/app.log.$(date +%F-%H%M)
sudo systemctl restart rsyslog

这些操作都有风险,执行前请确认变更窗口与回滚路径。


八:长期稳固策略(把临时救火变成可控)

  • 监控与告警:把 CPU/Memory/IO/网络关键指标纳入监控并设置合理阈值;关键阈值触发自动化脚本或工单。
  • 容量规划 & 压测:做持续的负载测试,找到系统的实际瓶颈并预留余量。
  • 服务分层 & 异步化:把耗时或不必要的同步调用改为异步,增加缓冲/队列(Kafka、RabbitMQ)。
  • 资源限制与 QoS:在容器平台上使用 requests/limits,避免小服务对大服务的「内耗」。
  • 定期剖析:把 perf、pprof、heap dumps 写入常规的 SRE 周期任务,发现隐匿问题。
  • 演练:故障演练要常态化,演练会暴露监控盲区与恢复流程的缺陷。

九:常见误区(我见过的“坑”)

  • • 盲目扩容:遇到问题第一反应是加机器,但如果根因是单进程锁或 DB 限制,扩容只是浪费。
  • • 频繁 drop_caches:乱用 drop_caches 会把缓存击穿,短期内看似缓解,长期反而伤性能。
  • • 忽略后端依赖:很多“应用慢”其实是依赖(DB、外部 API)慢,优化前端并不能从根本上解决问题。
  • • 只看 top:top 很直观,但容易误导;结合 iostat、vmstat、mpstat 才能分清 CPU/IO/内存的责任。

十:现场演练后的记录模板(务必填写)

每次事件结束后,请在工单里补全:

  • • 事件时间线:发生 → 发现 → 应急措施 → 恢复;
  • • 快速截图与关键命令输出(保存到工单附件);
  • • 根因分析结论与后续计划(谁、何时、如何修复);
  • • 是否需要变更 CI/CD、监控告警或自动化脚本。

结尾话

性能调优不是一次性的魔法,而是一种持续的工程:不断观察、不断剖析、不断改进。现场救火要快要稳,事后修复要彻底。你会发现,很多问题经过一次完整的定位与修复后,就再也不会重复抓你;这正是把“偶发事故”变成“可控变更”的意义。

老杨时间

这里我先声明一下,日常生活中大家都叫我波哥,跟辈分没关系,主要是岁数大了.就一个代称而已. 我的00后小同事我喊都是带哥的.张哥,李哥的. 但是这个称呼呀,在线下参加一些活动时.金主爸爸也这么叫就显的不太合适. 比如上次某集团策划总监,公司开大会来一句:“今个咱高兴!有请IT运维技术圈的波哥讲两句“ 这个氛围配这个称呼在互联网这行来讲就有点对不齐! 每次遇到这个情况我就想这么接话: “遇到各位是缘分,承蒙厚爱,啥也别说了,都在酒里了.我干了,你们随意!” 所以以后咱们改叫老杨,即市井又低调.还挺亲切,我觉得挺好.

运维X档案系列文章:

从告警到CTO:一个P0故障的11小时生死时速

企业级 Kubernetes 集群安全加固全攻略( 附带一键检查脚本)

老杨的关于AI的号

下一篇
举报
领券