Linux 性能排查分析完全教程
目录
- 性能排查方法论
- 系统全局概览工具
- [CPU 性能分析](#CPU 性能分析)
- 内存性能分析
- [磁盘 I/O 性能分析](#磁盘 I/O 性能分析)
- 网络性能分析
- 进程与线程深度分析
- 文件系统与内核分析
- 高级追踪工具
- 内核参数调优
- 性能监控体系搭建
- 经典案例实战
- 性能排查速查表
1. 性能排查方法论
1.1 USE 方法(Utilization, Saturation, Errors)
由 Brendan Gregg 提出,针对每一种资源检查三个维度:
| 维度 | 含义 | 示例 |
|---|---|---|
| Utilization(利用率) | 资源忙碌的时间占比 | CPU 使用率 90% |
| Saturation(饱和度) | 排队/溢出的工作 | runqueue 长度、swap 使用量 |
| Errors(错误) | 错误事件计数 | 磁盘 I/O 错误、网络丢包 |
对每种资源逐一排查:CPU → 内存 → 磁盘 → 网络 → 控制器 → 互联。
1.2 RED 方法(Rate, Errors, Duration)
面向服务/请求的排查:
- Rate:每秒请求数(QPS/TPS)
- Errors:每秒错误数
- Duration:每个请求的延迟分布
1.3 排查流程(60 秒法则)
登录服务器后,前 60 秒执行以下命令快速建立全局画像:
bash
# 1. 查看系统负载与运行时间
uptime
# 2. 查看内核日志(是否有 OOM、硬件错误)
dmesg -T | tail -50
# 3. 全局统计
vmstat 1 5
# 4. 磁盘 I/O
iostat -xz 1 3
# 5. 内存
free -h
# 6. 网络
sar -n DEV 1 3
# 7. 按 CPU 排序的进程
top -bn1 | head -20
# 8. 按内存排序的进程
ps aux --sort=-%mem | head -15
# 9. 按 I/O 排序的进程
iotop -bn1 | head -15
# 10. 查看是否有僵尸/不可中断进程
ps -eo pid,stat,comm | grep -E '^[[:space:]]*[0-9]+ [DZ]'
1.4 分层排查思维
性能问题可以按系统层级(硬件 → 内核 → 系统 → 应用)逐层下钻。本节用一张分层图帮助你建立『从现象到根因』的排查路径,后面所有工具都挂在对应的层级上。
┌─────────────────────────────────────────┐
│ 应用层 (Application) │
├─────────────────────────────────────────┤
│ 运行时/框架 (Runtime) │
├─────────────────────────────────────────┤
│ 系统调用 (Syscall) │
├─────────────────────────────────────────┤
│ 内核子系统 (Kernel Subsystems) │
│ 调度器 / 内存管理 / VFS / 网络协议栈 │
├─────────────────────────────────────────┤
│ 驱动层 (Drivers) │
├─────────────────────────────────────────┤
│ 硬件 (Hardware) │
└─────────────────────────────────────────┘
问题可能出现在任何一层,排查时自顶向下 定位瓶颈层,再自底向上确认根因。
1.5 关键原则
动手调优或排查前,先记住这几条原则,能避免大量无效操作和误判。
- 先观察,后干预:不要在未确认瓶颈前盲目调参。
- 一次只改一个变量:便于归因。
- 建立基线(Baseline) :没有对比就没有异常。
- 关注尾延迟(P99/P999) :平均值会掩盖问题。
- 生产环境慎用高开销工具 :如
strace -f全量跟踪。
2. 系统全局概览工具
2.1 top / htop
top 是 Linux 自带最基础的实时进程监视器,按 CPU、内存等资源对进程排序,一眼就能找到最忙的进程;htop 是其增强版,支持彩色、鼠标、树状视图和更友好的交互。当你想快速回答『现在系统里是谁在吃资源』时,第一个就该打开它。常用参数:-c 显示完整命令行、-H 展开线程、-p 锁定某进程。
bash
top -c # 显示完整命令行
top -H -p PID # 查看某进程的线程级 CPU
top -1 # 显示每个 CPU 核心
关键指标解读:
| 字段 | 含义 |
|---|---|
load average |
1/5/15 分钟平均运行队列长度(包含 D 状态) |
%us |
用户态 CPU |
%sy |
内核态 CPU |
%wa |
I/O wait(等待磁盘) |
%si/%hi |
软中断/硬中断 |
%st |
被 hypervisor 偷走的时间(虚拟机) |
判断标准:
- load average > CPU 核数 → 存在排队
%wa持续 > 20% → I/O 瓶颈%sy异常高 → 可能存在频繁系统调用或锁竞争%st> 0 → 宿主机资源争抢
2.2 vmstat
vmstat(virtual memory statistics)提供系统级整体快照:进程、内存、swap、I/O、中断、上下文切换和 CPU 的汇总。它最大的价值是『一眼看全局』------判断瓶颈在 CPU、内存还是 I/O,再决定往下用什么工具深挖。最常用 vmstat 1 每秒刷新一次观察趋势。
bash
vmstat 1 10 # 每秒采样,共 10 次
vmstat -w # 宽格式输出
vmstat -S m # 以 MB 为单位
关键列:
procs ───memory─── ──swap── ─────io───── ──system── ──────cpu─────
r b swpd free si so bi bo in cs us sy id wa st
| 列 | 关注点 |
|---|---|
r |
运行队列中等待 CPU 的进程数,持续 > 核数说明 CPU 不足 |
b |
不可中断睡眠(通常等 I/O)的进程数 |
si/so |
swap in/out,非零说明物理内存不足 |
bi/bo |
磁盘读写块数 |
in/cs |
中断次数 / 上下文切换次数 |
cs 突增 |
可能是锁竞争、频繁短任务 |
2.3 sar(System Activity Reporter)
sar(System Activity Reporter)是 sysstat 套件的核心,能采集并历史回放 CPU、内存、I/O、网络等几乎所有子系统指标,默认还会把数据落盘(/var/log/sa)。当你需要『昨天这个点系统什么样』『最近一周的趋势』时,sar 无可替代。注意它通常需要先安装 sysstat 并启动 sa1/sa2 采集服务。
前置:需安装 sysstat,并启用采集服务才能回放历史------RHEL/CentOS 要启动 sysstat 服务(其 sa1/sa2 通过 cron 定时采样落盘到 /var/log/sa/saXX),Debian/Ubuntu 默认已启用。若只装包没起服务,只能看实时、无法用 -f 回放历史。
bash
# 安装
yum install sysstat # RHEL/CentOS
apt install sysstat # Debian/Ubuntu
# CPU
sar -u 1 5
sar -P ALL 1 3 # 每个核心
# 内存
sar -r 1 5
# 磁盘
sar -d 1 5
# 网络
sar -n DEV 1 5 # 网卡流量
sar -n TCP,ETCP 1 5 # TCP 连接统计
sar -n SOCK 1 5 # socket 统计
# 历史数据(默认保存在 /var/log/sa/)
sar -u -f /var/log/sa/sa15 # 查看15号的数据
sar 输出怎么读:sar -u 看 %user/%system/%iowait/%idle,%iowait 高=CPU 在等 I/O;sar -r 关注 %memused 与 %commit(%commit > 100% 表示申请量超过物理内存,靠 swap/overcommit 撑着);sar -d 每块盘的 %util、await、tps;sar -n DEV 每张网卡的 rxpck/s txpck/s、rxkB/s txkB/s(注意这里 kB 是 1024 进制);sar -n TCP,ETCP 的 retrans/s(重传率,持续 >0 说明网络不稳)、estres/s(被动连接建立失败)。历史回放用 sar -f /var/log/sa/saXX(XX 为日期)。
2.4 dstat(多合一)
dstat 把 vmstat、iostat、netstat、ifstat 的能力合并成一个实时面板,用彩色分列同时展示 CPU、磁盘、网络、内存、系统中断等,最适合想在一屏里同时看多个子系统关联变化的场景。例如 dstat -cdngy 1 就是 CPU + 磁盘 + 网络 + 内存 + 系统每秒刷新。
bash
dstat -cdngy 1 5
# -c: CPU -d: disk -n: net -g: page -y: interrupt
dstat --top-cpu --top-mem --top-io
dstat 输出怎么读:每列对应一个子系统------默认含 CPU(usr/sys/idl/wai/hiq/siq)、磁盘(read/writ)、网络(recv/send)、分页(in/out)、系统(int 中断 / csw 上下文切换);加 -g 看分页、-y 看系统中断。直接看哪一列在动即可快速定位瓶颈域:CPU 列 wai 高→I/O 掣肘,网络列涨→网络繁忙。--top-cpu/--top-mem/--top-io 额外给出最耗资源的进程名。
2.5 atop(带历史回放)
atop 类似 top,但具备历史采样与回放能力:它会周期性记录系统/进程级资源占用到日志(默认 /var/log/atop/),你可以用 atop -r 回放任意历史时刻,定位半夜发生的性能尖刺这类事后排查。实时模式适合持续盯盘,回放模式适合事后取证。
bash
atop # 实时
atop -r /var/log/atop/atop_20240101 # 回放历史
# 按 'm' 切到内存视图, 'd' 磁盘, 'n' 网络
atop 输出怎么读:实时模式下顶栏是系统级 CPU/内存/磁盘/网络汇总,下方进程列表按资源排序,列含 CPU、MEM、DSK、NET 及 RDELAY/WDELAY(I/O 等待毫秒);按 m/d/n 分别切到内存/磁盘/网络专属视图。回放模式 atop -r <文件> 用 t/T 前后翻时间点,适合事后看某时刻谁最忙,是定位半夜尖刺的关键。
2.6 /proc 文件系统(一切数据的源头)
/proc 是一个由内核动态生成的虚拟文件系统,几乎所有性能工具(top、free、vmstat...)的数据源头都在这里。直接读 /proc 里的文件(如 /proc/stat、/proc/meminfo、/proc//*)可以绕过工具封装、拿到最原始最准确的指标。理解 /proc 等于掌握工具背后的真相。
bash
/proc/stat # CPU 统计
/proc/meminfo # 内存详情
/proc/loadavg # 负载
/proc/diskstats # 磁盘统计
/proc/net/dev # 网卡统计
/proc/net/sockstat # socket 统计
/proc/<pid>/stat # 进程状态
/proc/<pid>/status # 进程详细状态
/proc/<pid>/io # 进程 I/O 计数
/proc/<pid>/smaps # 进程内存映射详情
/proc/interrupts # 中断分布
/proc/softirqs # 软中断分布
/proc/pressure/ # PSI (Pressure Stall Information, kernel ≥ 4.20)
PSI(Pressure Stall Information)------ 内核 4.20+:
bash
cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/io
输出示例:
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
some:至少一个任务在等待该资源;full:所有任务都在等待。这是判断资源压力最直接的指标。
3. CPU 性能分析
3.1 CPU 问题分类
排查 CPU 前先对问题分类,有助于选对工具。本节用表格归纳常见的 CPU 问题类型、典型表现和可能原因,作为后续小节(定位、perf、火焰图...)的导航。
| 类型 | 表现 | 常见原因 |
|---|---|---|
| 用户态 CPU 高 | %us 高 |
算法低效、死循环、正则回溯 |
| 内核态 CPU 高 | %sy 高 |
频繁系统调用、锁竞争、中断处理 |
| I/O wait 高 | %wa 高 |
磁盘慢、NFS 卡住 |
| 中断风暴 | %hi/%si 高 |
网卡多队列未配置、硬件故障 |
| 上下文切换高 | cs 高 |
线程过多、锁竞争、频繁调度 |
| 单核打满 | 某一核 100% | 单线程瓶颈、中断绑定 |
3.2 定位高 CPU 进程/线程
当怀疑 CPU 是瓶颈时,第一步是定位------找到消耗 CPU 最高的进程乃至具体线程,再决定用 perf 等工具深入分析。本节给出从 top/ps 到线程级定位的实用命令链。
bash
# 找到 CPU 最高的进程
top -bn1 -o %CPU | head -15
# 找到该进程中 CPU 最高的线程
top -H -p <PID>
# 或
ps -mp <PID> -o THREAD,tid,time | sort -rn -k2 | head
# 将线程 ID 转为 16 进制(用于 jstack 等)
printf "%x\n" <TID>
3.3 perf ------ Linux 性能分析瑞士军刀
perf 是 Linux 内核自带的性能分析瑞士军刀,基于硬件 PMU(性能监控单元)和软件事件,能做 CPU 热点、函数级调用栈、缓存命中率、分支预测等分析,是定位『代码哪里慢』的终极工具。它分统计模式(perf stat)、采样模式(perf record + perf report)和实时模式(perf top)。需要先安装 linux-tools/perf 包。
前置与权限:需安装 linux-tools-common + linux-tools-$(uname -r)(Ubuntu)或 perf(RHEL);采集调用栈/符号通常需要 root 或 CAP_PERFMON(内核 5.8+),部分事件(如 cache-misses 的精确归因)还需安装对应内核的 debug 符号包(linux-image-*-dbgsym)。容器里默认无权限,需 --privileged 或赋予 CAP_PERFMON/CAP_BPF。
bash
#最标准、最推荐的方法(看发行版信息)
cat /etc/os-release
#怎么看结果:
#看到 ID=ubuntu 或 ID=debian → 用 apt 系列命令。
#看到 ID="rhel" 或 ID="centos" 或 ID="fedora" → 用 yum 或 dnf 系列命令。
# 安装
yum install perf # RHEL
apt install linux-tools-$(uname -r) # Ubuntu
# 查看 CPU 热点函数(采样 30 秒)
perf top -g
# 记录并分析
perf record -g -a sleep 30
perf report
# 只记录某进程
perf record -g -p <PID> sleep 10
# 统计硬件事件
perf stat -e cycles,instructions,cache-misses,branch-misses -p <PID> sleep 10
# 查看调度延迟
perf sched record sleep 10
perf sched latency
# 查看锁竞争(需要内核支持)
perf lock record sleep 10
perf lock report
perf stat 关键指标解读:
IPC (Instructions Per Cycle) < 1.0 → 可能存在内存瓶颈或分支预测失败
cache-misses / cache-references > 10% → 缓存效率差
branch-misses / branch-instructions > 5% → 分支预测差
3.4 火焰图(Flame Graph)
火焰图(Flame Graph)是 Brendan Gregg 提出的可视化调用栈方法,把 perf/栈采样数据画成横向条形图,宽度代表耗时占比,能一眼看出哪个函数/路径最耗时。它通常配合 perf 或 eBPF 采集栈样本后生成 SVG。注意采样要带 -g(调用图)才能拿到栈。
bash
# 安装 FlameGraph 工具
git clone https://github.com/brendangregg/FlameGraph.git
# 生成火焰图
perf record -F 99 -g -p <PID> sleep 30
perf script | ./FlameGraph/stackcollapse-perf.pl > out.folded
./FlameGraph/flamegraph.pl out.folded > flamegraph.svg
# 差分火焰图(对比优化前后)
./FlameGraph/difffolded.pl before.folded after.folded | \
./FlameGraph/flamegraph.pl > diff.svg
# 反向火焰图(从叶子到根)
./FlameGraph/flamegraph.pl --reverse out.folded > reverse.svg
3.5 上下文切换分析
上下文切换(context switch)过多会大量消耗 CPU 在切换而非干活上,常见于线程爆炸、锁竞争激烈、I/O 频繁等场景。本节通过 vmstat 的 cs 列和 pidstat -w 区分自愿/非自愿切换,定位切换来源。
bash
# 系统级
vmstat 1 # 看 cs 列
# 进程级
pidstat -w 1 5 # 显示每个进程的 cswch/s(自愿)和 nvcswch/s(非自愿)
# 非自愿切换高 → CPU 竞争激烈
# 自愿切换高 → 频繁等待锁/I/O
3.6 中断分析
硬件中断(IRQ)会打断 CPU 当前工作,过多中断(尤其网卡、磁盘)会让 CPU 陷入处理中断而非业务。通过 /proc/interrupts 和 /proc/softirqs 可观察中断分布,配合 irqbalance 或 CPU 亲和性优化。
bash
# 查看中断分布
cat /proc/interrupts
watch -n1 cat /proc/interrupts # 动态观察
# 查看软中断
cat /proc/softirqs
# 查看中断亲和性
cat /proc/irq/<IRQ>/smp_affinity_list
# 修改中断亲和性(绑定到 CPU 0 和 1)
echo 3 > /proc/irq/<IRQ>/smp_affinity
# 使用 irqbalance(自动均衡)
systemctl status irqbalance
# 使用 tuned 或手动脚本做网卡多队列中断绑定
3.7 调度器分析
进程在 CPU 上如何排队、何时被抢占,由内核调度器(CFS 等)决定。调度延迟过高会让进程该跑却没被调度到。本节查看调度策略(chrt)、调度统计(/proc//sched)以及 schedstat,用于诊断调度饥饿或优先级问题。
bash
# 查看调度策略
chrt -p <PID>
# 查看进程的调度统计
cat /proc/<PID>/sched
# 查看 CPU 频率调节
cpupower frequency-info
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# 设置为 performance(避免节能降频影响延迟)
cpupower frequency-set -g performance
3.8 NUMA 分析
现代多路服务器是 NUMA 架构:CPU 访问本地内存快、远端内存慢。进程内存分配跨 NUMA 节点会显著拖慢性能。用 numactl --hardware、numastat 查看拓扑与跨节点访问,必要时用 numactl 绑定或设置内存策略。
bash
# 查看 NUMA 拓扑
numactl --hardware
lscpu | grep NUMA
# 查看 NUMA 内存分配
numastat -p <PID>
# 查看 NUMA 命中率
numastat
# 如果 numa_miss 持续增长,考虑绑定进程到本地 NUMA 节点
numactl --cpunodebind=0 --membind=0 <command>
3.9 运行队列深度
运行队列(run queue)长度反映了想跑但等 CPU 的进程数。队列持续过长说明 CPU 算力不足或调度不均。可从 /proc/schedstat、mpstat、top 的 load average 等多角度观察。
bash
# 每个 CPU 的运行队列
cat /proc/schedstat | awk 'NR<=3'
# 或使用 mpstat
mpstat -P ALL 1 3
# 查看某 CPU 上等待的进程
ps -eo pid,psr,stat,comm | awk '$3~/R/'
4. 内存性能分析
4.1 内存问题分类
内存类问题表现各异(泄漏、回收、OOM、缓存...),本节用表格归类典型现象与根因,帮助你在 4.2--4.8 各小节间快速定位。
| 类型 | 表现 | 常见原因 |
|---|---|---|
| 内存不足 | swap 频繁使用、OOM | 内存泄漏、配置不当 |
| 内存泄漏 | RSS 持续增长不回收 | 代码 bug、缓存未设上限 |
| 页缓存回收 | kswapd 高、direct reclaim | 内存压力大 |
| 大页碎片化 | 分配 THP 延迟高 | 内存碎片 |
| NUMA 跨节点 | 远端内存访问延迟高 | 未绑定 NUMA |
4.2 free / /proc/meminfo
free 快速展示物理内存、swap 的使用概况;/proc/meminfo 则给出几十项内存明细(Cached、Buffers、SReclaimable、AnonPages...)。理解 meminfo 各项是读懂一切内存问题的前提------free 只是入口,meminfo 才是真相。注意 Linux 会尽量用内存做缓存,available 比 free 更能反映真正可用内存。
bash
free -h
total used free shared buff/cache available
Mem: 125Gi 89Gi 2.1Gi 1.2Gi 33Gi 34Gi
Swap: 8.0Gi 1.2Gi 6.8Gi
关键理解:
buff/cache:可回收的缓存,不算真正"被占用"available:内核估算的可用于新进程的内存(含可回收缓存)shared:tmpfs + shmem,不可回收- 真正可用 ≈
available,而非free
bash
# 详细内存信息
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Dirty|Writeback|AnonPages|Mapped|Shmem|Slab|SReclaimable|SUnreclaim|PageTables|KernelStack|Committed"
4.3 内存泄漏排查
内存泄漏表现为进程 RSS 随时间只增不减,最终触发 OOM 或拖慢系统。排查思路是持续监控进程 RSS 变化、对比基线、定位到具体模块。本节给出 watch + ps、smem、以及结合 pmap 的定位方法。
bash
# 监控进程 RSS 变化
watch -n5 'ps -o pid,rss,vsz,comm -p <PID>'
# 持续记录
while true; do
echo "$(date) $(ps -o rss= -p <PID>)" >> /tmp/mem_track.log
sleep 60
done
# 查看进程内存映射
cat /proc/<PID>/smaps | grep -E "^Size|^Rss|^Pss" | awk '{arr[$1]+=$2} END {for (a in arr) print a, arr[a]}' | sort -k2 -rn
# pmap 查看内存段
pmap -x <PID> | sort -k3 -rn | head -20
# 查看堆内存(需要 glibc malloc debug)
gdb -p <PID>
(gdb) call malloc_stats()
(gdb) call malloc_info(0, stdout)
内核态内存泄漏:
bash
# 查看 slab 分配器
slabtop -o | head -20
cat /proc/slabinfo | sort -k3 -rn | head
# 使用 kmemleak(需要内核编译开启)
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak
4.4 Swap 与回收
当物理内存紧张,内核会把不常用页换出到 swap(磁盘),或触发页回收(kswapd)。swap 过多使用意味着内存真正不够,且访问被换出页会引发明显卡顿。本节查看 swapon、回收压力(/proc/pressure/memory),并解释 vm.swappiness 的作用。
bash
# 查看 swap 使用
swapon -s
free -h
# 查看回收压力
cat /proc/pressure/memory
# 查看 kswapd 活动
sar -B 1 5
# pgpgin/s, pgpgout/s: 页面换入换出
# pgscan/s: 扫描的页面数(高说明回收压力大)
# pgsteal/s: 成功回收的页面数
# vmstat 的 si/so 列
vmstat 1
关键参数:
bash
# swappiness: 0-100(全局;cgroup v2 的 memory.swappiness 才支持 0-200),控制 swap 倾向(默认 60)
cat /proc/sys/vm/swappiness
# 数据库服务器通常设为 1 或 10
echo 1 > /proc/sys/vm/swappiness
# 脏页回写阈值
cat /proc/sys/vm/dirty_ratio # 默认 20%,超过后同步回写
cat /proc/sys/vm/dirty_background_ratio # 默认 10%,后台回写启动
# OOM 相关
cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/panic_on_oom
4.5 OOM 分析
OOM(Out-Of-Memory)时内核的 OOM Killer 会杀掉占用内存最高的进程以保全系统。本节教你从 dmesg、内核日志中解读 OOM 事件:谁被杀了、当时内存分布如何、是 cgroup 限制还是整机耗尽。
bash
# 查看 OOM 日志
dmesg -T | grep -i "oom\|killed process"
journalctl -k | grep -i oom
# 查看进程的 OOM 分数
cat /proc/<PID>/oom_score
cat /proc/<PID>/oom_score_adj # 可调整,-1000 表示不杀
# 保护关键进程
echo -1000 > /proc/<PID>/oom_score_adj
4.6 页缓存与 Buffer
Linux 用 page cache 缓存文件数据、用 buffer 缓存块设备元数据,这是内存越大文件 IO 越快的原因。缓存占用高通常不是问题(可回收),但某些场景下需要主动释放或限制。本节解读 /proc/meminfo 中 Cached/Buffer 含义及 drop_caches 的正确用法。
bash
# 查看页缓存占用
cat /proc/meminfo | grep -E "Cached|Buffers"
# 手动释放页缓存(生产慎用,会导致后续 I/O 突增)
sync
echo 1 > /proc/sys/vm/drop_caches # 释放页缓存
echo 2 > /proc/sys/vm/drop_caches # 释放 dentries 和 inodes
echo 3 > /proc/sys/vm/drop_caches # 释放所有
# 查看文件的缓存状态
vmtouch /path/to/file
vmtouch -v /path/to/directory
4.7 大页(HugePages)
标准 4KB 页在管理大内存时会产生大量 TLB miss 和页表开销;HugePages(2MB/1GB)能显著减少 TLB miss,对数据库、JVM、DPDK 等内存密集型应用收益明显。本节查看与配置 HugePages(/proc/meminfo 的 Huge*、/sys/kernel/mm/hugepages、grub/kernel 参数)。
bash
# 查看大页状态
cat /proc/meminfo | grep -i huge
cat /sys/kernel/mm/transparent_hugepage/enabled
# 配置静态大页(如 Oracle/Redis 需要)
echo 4096 > /proc/sys/vm/nr_hugepages # 分配 4096 个 2MB 大页
# THP(透明大页)对数据库通常有害
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 持久化配置
grubby --update-kernel=ALL --args="transparent_hugepage=never"
4.8 内存带宽与延迟
当 CPU 算力充足但应用仍慢,瓶颈可能在内存墙------内存带宽耗尽或访问延迟过高(NUMA 跨节点、频繁 cache line 争用)。本节用 Intel MLC(Memory Latency Checker)等工具测量带宽/延迟,定位是否内存子系统成为天花板。
bash
# 使用 Intel MLC (Memory Latency Checker)
./mlc --bandwidth_matrix
./mlc --loaded_latency
# 使用 perf 查看内存访问模式
perf stat -e LLC-load-misses,LLC-store-misses -p <PID> sleep 10
# 使用 numactl 测试跨 NUMA 延迟
numactl --hardware # 查看节点间距离
5. 磁盘 I/O 性能分析
5.1 I/O 问题分类
I/O 瓶颈分磁盘吞吐、延迟、IOPS、文件系统、网络存储等多个维度,本节表格归类,作为 5.2--5.10 的索引。
| 类型 | 表现 | 常见原因 |
|---|---|---|
| 吞吐瓶颈 | IOPS/带宽打满 | 磁盘慢、RAID 降级 |
| 延迟高 | await 高 | 队列深、磁盘慢 |
| I/O 调度不当 | 某些进程饥饿 | 调度器不匹配工作负载 |
| 文件系统瓶颈 | 元数据操作慢 | journal 争抢、碎片 |
| 写放大 | 实际写入 >> 逻辑写入 | 日志、COW、对齐问题 |
5.2 iostat
iostat(sysstat 套件)是分析块设备 I/O 的标配,输出每个磁盘的 %util、await、r/s、w/s、rKB/s 等。核心看 %util(设备忙否)和 await/r_await/w_await(延迟)。最常用 iostat -xz 1,配合 -d 只看设备。
bash
iostat -xz 1 5
关键列解读:
| 列 | 含义 | 关注阈值 |
|---|---|---|
r/s, w/s |
每秒读/写 IOPS | 对比磁盘规格 |
rkB/s, wkB/s |
每秒读/写吞吐 | 对比磁盘带宽 |
rrqm/s, wrqm/s |
合并的请求数 | 高说明顺序 I/O |
await |
平均 I/O 等待时间(ms) | HDD > 20ms 关注,SSD > 5ms 关注 |
r_await, w_await |
读/写分别的等待时间 | |
avgqu-sz |
平均队列深度 | > 磁盘并发能力则饱和 |
%util |
设备利用率 | 100% 不一定饱和(SSD 可并发) |
aqu-sz |
平均活动 I/O 数 |
注意: %util 对 SSD/NVMe 意义有限,因为它们支持高并发。应结合 await 和队列深度判断。
5.3 iotop(进程级 I/O)
iostat 看的是设备,iotop 看的是进程------它像 top 一样按进程/线程展示实时磁盘读写速率,帮你回答是哪个进程在狂写磁盘。常用 -o(只显示有 IO 的)、-P(按进程)、-a(累计)。
bash
iotop -oPa # 只显示有 I/O 的进程,累计
iotop -obtqqq -d 1
iotop 输出怎么读:默认列含 DISK READ、DISK WRITE(瞬时速率)、SWAPIN、IO(进程等待 I/O 的时间占比);-o 只看有 I/O 的进程、-P 按进程聚合(否则按线程)、-a 显示累计量。持续 IO> 列偏高的进程即为 I/O 热点;SWAPIN 高说明在大量换入,通常伴内存压力。
5.4 pidstat(进程 I/O 统计)
pidstat(sysstat)是进程级全能统计工具,可分别看进程的 CPU(-u)、内存(-r)、I/O(-d)、上下文切换(-w)、线程(-t)。当你已锁定某进程、要量化它的各项资源消耗趋势时,pidstat 1 5 是日常首选。
bash
pidstat -d 1 5 # 每个进程的 kB_rd/s, kB_wr/s
pidstat -d -p <PID> 1
pidstat 输出怎么读:-d 给出每进程的 kB_rd/s(读吞吐)、kB_wr/s(写吞吐)、kB_ccwr/s(因脏页回收而取消的写);-u 给 %usr/%system/%guest 与 CPU 占用;-w 给 cswch/s(自愿切换,多因等锁/I/O)与 nvcswch/s(非自愿,CPU 被抢占);-r 给 minflt/s majflt/s(缺页)、VSZ RSS;加 -t 可下钻到线程级。每项都带时间窗口(如 1 5 表示每秒采样共 5 次)。
5.5 blktrace / btrace(块层追踪)
blktrace 在块设备层做精细化追踪,记录每个 I/O 请求从提交、入队、合并、分发到完成的全生命周期,再用 blkparse/btt 分析各阶段耗时。当 iostat 显示 await 很高、你想知道时间到底耗在队列还是设备时,它就是答案。
前置与权限:需 blktrace/blkparse 包,且 debugfs 已挂载(通常 /sys/kernel/debug);运行需 root。btrace 是其简化封装。trace 数据量大,建议定向到文件并限定时间(如 blktrace -d /dev/sda -w 10)。
bash
# 抓取块设备 I/O 事件
blktrace -d /dev/sda -o - | blkparse -i - > blktrace.txt
# 或使用 btrace(简化版)
btrace /dev/sda
# 分析工具
btt -i sda.blktrace.* # 生成延迟统计
5.6 I/O 调度器
内核 I/O 调度器决定块请求如何合并与排序,影响吞吐与延迟的取舍。常见调度器:mq-deadline(通用)、none/noop(SSD/NVMe)、bfq(桌面交互)。本节查看与切换调度器(/sys/block//queue/scheduler),并为不同介质给建议。
bash
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# [mq-deadline] kyber bfq none
# 修改
echo mq-deadline > /sys/block/sda/queue/scheduler
# 推荐:
# SSD/NVMe → none 或 mq-deadline
# HDD → mq-deadline 或 bfq
# 数据库 → mq-deadline 或 none
5.7 文件系统级分析
同样一块磁盘,不同文件系统的碎片、日志、特性会带来不同性能。本节从文件系统视角排查:filefrag(碎片)、df/挂载选项、inode 耗尽、日志模式等,弥补只看设备指标的盲区。
bash
# ext4 碎片检查
filefrag -v /path/to/file
# XFS 碎片
xfs_db -r -c frag /dev/sda1
# 文件系统 I/O 延迟追踪
# 使用 eBPF/bcc 工具(见后文)
# 查看文件系统挂载选项
mount | grep /data
cat /proc/mounts
# 关键选项:
# noatime: 不更新访问时间(减少写)
# nobarrier: 关闭写屏障(SSD 有电容时可考虑)
# data=writeback: ext4 减少 journal 写入
5.8 磁盘健康检查
磁盘本身老化或故障会表现为诡异的 I/O 延迟/错误。用 smartctl 读取 SMART 健康信息(重分配扇区、温度、通电时间)可提前发现坏盘;结合 dmesg 中的 ATA/SCSI 错误判断是否硬件问题。
bash
# SMART 信息
smartctl -a /dev/sda
smartctl -H /dev/sda # 快速健康检查
# 查看坏道重映射
smartctl -a /dev/sda | grep -i "reallocated\|pending\|uncorrect"
# 查看 I/O 错误
dmesg | grep -i "error\|fault\|reset\|timeout" | grep -i "sd\|nvme\|blk"
cat /sys/block/sda/stat # 注意:该文件无错误计数字段(第10列为 io_ticks)。磁盘错误看 dmesg / smartctl
5.9 NVMe 专项
NVMe 固态盘延迟低至微秒、队列深度大,传统 HDD 调优思路不适用。本节用 nvme-cli 查看设备信息、SMART、命名空间、固件,并针对 NVMe 给出中断亲和、队列、IO 调度等专项建议。
bash
# NVMe 设备信息
nvme list
nvme smart-log /dev/nvme0n1
# NVMe 队列深度
cat /sys/block/nvme0n1/queue/nr_requests
# NVMe 命名空间
nvme id-ns /dev/nvme0n1
5.10 写回与刷盘
脏页(dirty page)由内核回写线程(pdflush/Writeback)按阈值周期性刷到磁盘。刷盘策略不当会导致突发卡顿或数据丢失风险。本节解读 /proc/meminfo 的 Dirty、/proc/sys/vm/dirty_* 参数,以及回写线程状态。
bash
# 查看脏页
cat /proc/meminfo | grep Dirty
# 查看回写线程
ps aux | grep -E "flush|writeback"
# 调整回写参数
echo 10 > /proc/sys/vm/dirty_background_ratio
echo 20 > /proc/sys/vm/dirty_ratio
echo 500 > /proc/sys/vm/dirty_expire_centisecs # 脏页存活时间(1/100秒)
echo 1000 > /proc/sys/vm/dirty_writeback_centisecs # 回写线程唤醒间隔
6. 网络性能分析
6.1 网络问题分类
网络问题涵盖吞吐、延迟、丢包、连接数、DNS、TCP 异常等,本节表格归类,作为 6.2--6.9 的导航。
| 类型 | 表现 | 常见原因 |
|---|---|---|
| 带宽打满 | 吞吐量接近网卡上限 | 流量过大、未做限流 |
| 延迟高 | RTT 增大 | 路由问题、队列缓冲 |
| 丢包 | retransmit 增加 | 拥塞、网卡 ring buffer 溢出 |
| 连接数多 | TIME_WAIT 堆积 | 短连接过多 |
| 软中断高 | %si 高、ksoftirqd 忙 | 单核处理所有中断 |
6.2 基础网络统计
排查网络先要有整体视图:网卡流量、错误/丢包、协议分布、吞吐。本节汇总 sar -n、ifstat、nload、/proc/net/dev 等基础统计手段,快速判断网络是否饱和或存在错误。
bash
# 网卡流量
sar -n DEV 1 5
ifstat 1
nload eth0
# 网络接口错误
ip -s link show eth0
ethtool -S eth0 | grep -i "error\|drop\|miss\|fifo"
# 协议栈统计
cat /proc/net/softnet_stat
# 第 1 列: 处理的包数
# 第 2 列: 丢弃的包数(非零 = 队列溢出)
# 第 3 列: time_squeeze(软中断预算用完)
nstat -az | grep -i "drop\|error\|overflow\|retrans"
netstat -s | grep -i "error\|drop\|retrans\|overflow"
6.3 TCP 连接分析
ss 是取代 netstat 的现代工具,能高效列出套接字状态、连接数、端口占用。本节用 ss -s 看整体连接统计、ss -tan 按状态归类,定位 TIME_WAIT 堆积、端口耗尽、半连接等问题。
bash
# 连接状态统计
ss -s
ss -tan | awk 'NR>1{print $1}' | sort | uniq -c | sort -rn
# 查看 TIME_WAIT
ss -tan state time-wait | wc -l
# 查看 ESTABLISHED 连接
ss -tan state established | wc -l
# 查看某端口的连接
ss -tanp | grep :8080
# 查看 TCP 重传
nstat -az TcpRetransSegs
netstat -s | grep retrans
# 查看 TCP 队列溢出
nstat -az TcpExtListenOverflows TcpExtListenDrops
netstat -s | grep "listen"
ss 输出怎么读:ss -s 是汇总(总 TCP、ESTAB、TIME-WAIT 等数量);ss -tan 每行 State + 本地:端口 + 对端:端口 + Recv-Q/Send-Q------对 ESTABLISHED 连接,Recv-Q 是已收但未交给应用的字节、Send-Q 是已发但未获对端确认(ACK)的字节,二者持续非零说明收发不及时(应用慢或网络拥塞);ss -tanp 额外显示进程。常用状态:ESTAB 已建立、TIME-WAIT 主动关闭方等待、CLOSE-WAIT 被动方未关、SYN-SENT/RECV 握手中、LISTEN 监听。
6.4 抓包与深度分析
tcpdump 在网络接口抓包,是定位包到底有没有到/走了什么的最终手段;tshark(命令行 Wireshark)可在服务端做协议级深度解析。本节给出常用抓包过滤表达式与保存/回放方法。
bash
# tcpdump 抓包
tcpdump -i eth0 -nn -c 100 port 80
tcpdump -i eth0 -w /tmp/capture.pcap # 保存
# 分析重传
tshark -r capture.pcap -Y "tcp.analysis.retransmission"
# 分析延迟
tshark -r capture.pcap -q -z conv,tcp
# 使用 Wireshark GUI 分析
scp capture.pcap user@local:~
6.5 网络延迟排查
网络慢可能是链路延迟、路由绕路、或中间设备限速。本节用 ping(RTT)、traceroute/mtr(逐跳延迟)、以及 TCP 层面的重传/RTT 分析,分层定位延迟来源。
bash
# ping 延迟
ping -c 100 target_ip | tail -1
# 路由追踪
traceroute target_ip
mtr -r -c 100 target_ip
# TCP 连接延迟
curl -o /dev/null -s -w "DNS: %{time_namelookup}\nConnect: %{time_connect}\nTTFB: %{time_starttransfer}\nTotal: %{time_total}\n" http://target
# 使用 tcpretrans (bcc) 追踪重传
tcpretrans
6.6 网卡调优
网卡硬件参数是高吞吐(尤其 10G/25G+)场景的关键:ring buffer、offload(TSO/GRO/LRO)、中断亲和、队列数。本节用 ethtool 查看/调整这些参数,缓解丢包与 CPU 软中断瓶颈。
bash
# 查看 ring buffer
ethtool -g eth0
ethtool -G eth0 rx 4096 tx 4096
# 多队列
ethtool -l eth0
ethtool -L eth0 combined 8
# 中断绑定(将不同队列绑定到不同 CPU)
# 查看 IRQ 号
grep eth0 /proc/interrupts
# 绑定
echo 2 > /proc/irq/<IRQ>/smp_affinity_list
# 使用 RPS/RFS(软件多队列)
echo ffff > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
# 关闭 GRO/LRO(排查时)
ethtool -K eth0 gro off lro off
# 开启/关闭 offload
ethtool -K eth0 tso on gso on
6.7 内核网络参数
大量网络性能/稳定性行为由内核 sysctl 参数控制:TCP 缓冲区大小、TIME_WAIT 复用、backlog、conntrack 限制等。本节列出最常用的一批 net.* 参数及其调优含义。
bash
# TCP 缓冲区
sysctl net.core.rmem_max net.core.wmem_max
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem
# 连接队列
sysctl net.core.somaxconn # listen 队列上限
sysctl net.ipv4.tcp_max_syn_backlog
# TIME_WAIT 相关
sysctl net.ipv4.tcp_tw_reuse # 允许复用 TIME_WAIT
sysctl net.ipv4.tcp_max_tw_buckets
# 连接跟踪(conntrack)
sysctl net.netfilter.nf_conntrack_max
sysctl net.netfilter.nf_conntrack_tcp_timeout_established
cat /proc/sys/net/netfilter/nf_conntrack_count
# 如果 conntrack 表满
dmesg | grep "table full"
echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max
6.8 网络带宽测试
iperf3 是标准的端到端带宽/吞吐测试工具,分服务端(-s)与客户端(-c),可测 TCP/UDP 带宽、抖动、丢包。本节给出点对点带宽压测的基本用法,用于验证链路真实容量。
bash
# iperf3
iperf3 -s # 服务端
iperf3 -c server_ip -t 30 -P 4 # 客户端,4 并发,30 秒
# 测试 UDP 丢包
iperf3 -c server_ip -u -b 1G
# 测试延迟
sockperf ping-pong -i server_ip -p 11111
6.9 DNS 排查
DNS 解析慢或失败会表现为应用卡在连接前。用 dig、nslookup、time 测量解析耗时,检查 resolv.conf、DNS 服务器、缓存(nscd/systemd-resolved),定位解析瓶颈。
bash
# 测试 DNS 解析时间
dig example.com
time nslookup example.com
# 查看 nscd/systemd-resolved 缓存
systemctl status systemd-resolved
resolvectl statistics
# strace 查看 DNS 系统调用(grep ":53" 即按 DNS 服务端口 53 过滤网络系统调用)
strace -e trace=network -p <PID> 2>&1 | grep ":53"
7. 进程与线程深度分析
7.1 进程状态解读
进程状态(R/S/D/Z/T)藏着大量线索:D 是不可中断睡眠(常卡在 I/O)、Z 是僵尸、R 排队说明 CPU 不够。本节解读 ps 状态字段、wchan(卡在哪个内核函数),是进程级排查的基本功。
bash
ps -eo pid,ppid,user,stat,wchan:32,pcpu,pmem,rss,vsz,etime,comm | head -20
| STAT | 含义 |
|---|---|
| R | Running(运行中或可运行) |
| S | Sleeping(可中断睡眠) |
| D | Disk sleep(不可中断睡眠,通常等 I/O) |
| Z | Zombie(僵尸进程) |
| T | Stopped(被信号停止) |
| I | Idle(内核空闲线程) |
| + | 前台进程组 |
| < | 高优先级 |
| N | 低优先级 |
| L | 有页面锁定在内存 |
大量 D 状态进程 → I/O 问题或 NFS 挂载卡住。
7.2 strace(系统调用追踪)
strace 追踪进程发出的系统调用(open/read/write/connect...)及其返回值、耗时,是应用卡在哪一步的利器。-c 做统计(低开销)、-p 挂到运行进程、-T 看每次调用耗时、-e 过滤 syscall。注意它会显著拖慢目标进程。
前置与权限:需 ptrace 权限(root 或 CAP_SYS_PTRACE)。注意容器环境常受限:若启用 seccomp 或 kernel.yama.ptrace_scope=1,strace 可能只能追踪自身派生的子进程,无法附加到已有进程(-p 失败)。高负载进程用 -f 全量跟踪会严重拖慢目标,优先用 -c 统计或 -e trace= 收窄。
bash
# 追踪进程的系统调用
strace -p <PID> -c # 统计模式(低开销)
strace -p <PID> -tt -T # 显示时间戳和耗时
strace -p <PID> -e trace=network # 只追踪网络相关
strace -p <PID> -e trace=file # 只追踪文件操作
strace -p <PID> -e trace=%desc # 文件描述符相关
# 追踪并附加子进程
strace -f -p <PID>
# 追踪新启动的命令
strace -f -tt -o /tmp/trace.log <command>
# 统计系统调用耗时
strace -cp <PID> sleep 10
strace 输出示例解读:
14:23:01.123456 read(3, "data...", 4096) = 4096 <0.000012>
# fd 缓冲区 大小 返回值 耗时12微秒
⚠️ 警告: strace 开销极大(10-100x 减速),生产环境慎用,尽量用
-c 统计模式或限定-e trace=。
7.3 ltrace(库函数追踪)
ltrace 类似 strace,但追踪的是用户态库函数调用(如 malloc/free/printf),适合定位库层面而非系统调用层面的行为(如内存分配热点)。常用 -c 统计、-e 过滤。
bash
ltrace -p <PID> -c
ltrace -p <PID> -e malloc,free,open
7.4 /proc/ 深度挖掘
每个进程在 /proc// 下都有一整套内核暴露的信息:status(内存/线程)、fd(打开的文件描述符)、maps(内存映射)、io(读写统计)、stack(内核栈)、cmdline。本节演示如何直接读这些文件做深度进程画像,无需额外工具。
bash
PID=12345
# 基本信息
cat /proc/$PID/status
cat /proc/$PID/stat
# 打开的文件描述符
ls -la /proc/$PID/fd | wc -l
ls -la /proc/$PID/fd | head -20
# 文件描述符限制
cat /proc/$PID/limits | grep "open files"
# 内存映射
cat /proc/$PID/maps | head -30
cat /proc/$PID/smaps_rollup
# I/O 统计
cat /proc/$PID/io
# 线程
ls /proc/$PID/task/
cat /proc/$PID/task/*/stat | awk '{print $1, $2, $14+$15}' | sort -rn -k3 | head
# 网络连接
cat /proc/$PID/net/tcp | wc -l
# cgroup 归属
cat /proc/$PID/cgroup
# 环境变量
cat /proc/$PID/environ | tr '\0' '\n'
# 工作目录和可执行文件
ls -la /proc/$PID/cwd /proc/$PID/exe
# 信号屏蔽
cat /proc/$PID/status | grep Sig
7.5 僵尸进程处理
僵尸进程(Z 状态)已退出但父进程没回收其资源,大量堆积会耗尽 PID。本节给出定位僵尸及其父进程、通过重启父进程或发送 SIGCHLD 来清理的方法。
bash
# 找到僵尸进程
ps -eo pid,ppid,stat,comm | grep Z
# 找到其父进程
ps -o pid,ppid,comm -p <zombie_pid>
# 通知父进程回收
kill -SIGCHLD <parent_pid>
# 如果父进程不处理,杀父进程让 init 回收
kill <parent_pid>
7.6 线程分析
现代服务多为多线程,瓶颈常在某几个线程。本节用 ps -T、top -H 查看线程、结合 /proc//task 分析线程栈与资源,定位哪个线程在忙。
bash
# 查看进程的所有线程
ps -T -p <PID>
top -H -p <PID>
# 查看线程栈
cat /proc/<PID>/task/<TID>/stack # 内核栈(需 root)
# 用户态栈
gdb -p <PID>
(gdb) info threads
(gdb) thread apply all bt
# Java 进程
jstack <PID>
jstack -l <PID> > /tmp/thread_dump.txt
# Go 进程
kill -SIGQUIT <PID> # 打印 goroutine 栈到 stderr
# 或设置 GOTRACEBACK=all
# Python 进程
py-spy dump --pid <PID>
py-spy top --pid <PID>
7.7 文件描述符泄漏
进程或系统打开的 fd 超过限制会导致 too many open files。本节查看进程 fd 数(/proc//fd)、系统级限制(fs.file-nr、ulimit -n),定位 fd 泄漏(常见于连接/文件未关闭)。
bash
# 查看进程 fd 数量
ls /proc/<PID>/fd | wc -l
# 查看系统级 fd
cat /proc/sys/fs/file-nr
# 已分配 未使用 最大值
# 查看 fd 限制
ulimit -n
cat /proc/sys/fs/file-max
# 查看谁打开了某文件
lsof /path/to/file
fuser -v /path/to/file
# 查看已删除但仍被占用的文件(空间未释放)
lsof +L1 | grep deleted
8. 文件系统与内核分析
8.1 文件系统性能
文件系统是 I/O 之上的抽象层,其挂载选项、日志模式、特性直接影响性能。本节从文件系统层面排查(挂载参数、journal、特性开关),与 5.x 设备层形成互补。
bash
# 查看文件系统类型和选项
df -Th
mount | grep /data
# inode 使用
df -i
# 目录项缓存
cat /proc/sys/fs/dentry-state
cat /proc/sys/fs/inode-nr
# 文件系统 I/O 延迟(bcc 工具)
ext4slower 10 # 显示 >10ms 的 ext4 操作
xfsslower 10
biolatency # 块设备 I/O 延迟直方图
biosnoop # 每次 I/O 的详细信息
8.2 VFS 层分析
VFS(虚拟文件系统)是应用与具体文件系统之间的统一抽象层。VFS 层的锁竞争、dentry 缓存、inode 争用都可能成为瓶颈。本节用 trace/perf 观察 VFS 相关函数开销。
bash
# 使用 bcc 工具
vfsstat 1 # VFS 操作统计
vfscount 10 # 10 秒内 VFS 调用计数
fileslower 10 # 慢文件操作
filetop -C 5 # 按文件统计 I/O
# 使用 ftrace(先进入 tracefs 挂载点,与 9.3 ftrace 用法风格统一)
cd /sys/kernel/debug/tracing
echo 1 > events/fs/enable
cat trace_pipe
8.3 锁与同步分析
内核或应用中的锁竞争会让并行变串行,表现为 CPU 空转等待。本节用 perf、eBPF 等观察锁的持有/等待时间(如 futex、spinlock、rwsem),定位同步瓶颈。
bash
# 内核锁竞争(需要 CONFIG_LOCKDEP 或 CONFIG_LOCK_STAT)
cat /proc/lock_stat
# 使用 perf
perf lock record sleep 10
perf lock report
# 使用 bcc
funclatency 'mutex_lock' 10 # mutex_lock 延迟分布
# 用户态锁竞争(需要应用支持)
# Java: jstack 查看 BLOCKED 线程
# C/C++: 使用 valgrind --tool=helgrind 或 ThreadSanitizer
8.4 内核日志分析
dmesg / journalctl 记录了内核的运行事件与警告:OOM、I/O 错误、软锁、RCU stall、硬件异常。本节归纳排查性能问题时最该 grep 的关键词与套路。
bash
dmesg -T --level=err,warn | tail -50
journalctl -k -p warning --since "1 hour ago"
# 常见关键信息:
# "Out of memory" → OOM
# "I/O error" → 磁盘问题
# "BUG:" / "WARNING:" → 内核 bug
# "hung_task" → 任务卡住超过 120 秒
# "soft lockup" → CPU 被占用超过 20 秒未调度
# "RCU detected stall" → RCU 回调积压
# "nf_conntrack: table full" → 连接跟踪表满
8.5 hung task 分析
内核 hung task 指某任务在 D 状态(不可中断)超过阈值(默认 120s),通常卡在 I/O 或锁上。本节解读 hung task 告警、结合栈与块设备/I/O 状态定位根因。
bash
# 内核参数:超过 120 秒处于 D 状态则报告
cat /proc/sys/kernel/hung_task_timeout_secs
# dmesg 中搜索
dmesg | grep -A 20 "hung_task"
# 查看 D 状态进程的内核栈
for pid in $(ps -eo pid,stat | awk '$2~/D/{print $1}'); do
echo "=== PID $pid ==="
cat /proc/$pid/stack
cat /proc/$pid/wchan
echo
done
8.6 cgroup 分析
cgroup(尤其 cgroup v2)对 CPU、内存、I/O、进程数设限,容器/systemd 都依赖它。进程被限流常源于 cgroup 配额(cpu.max、memory.max、io.max)。本节查看 cgroup 层级与当前用量,排查整机有资源但容器用不上的问题。
bash
# cgroup v2 查看资源使用
cat /sys/fs/cgroup/system.slice/<service>.service/cpu.stat
cat /sys/fs/cgroup/system.slice/<service>.service/memory.current
cat /sys/fs/cgroup/system.slice/<service>.service/io.stat
# cgroup v1
cat /sys/fs/cgroup/cpu/docker/<container_id>/cpuacct.usage
cat /sys/fs/cgroup/memory/docker/<container_id>/memory.usage_in_bytes
# systemd-cgtop(类似 top 的 cgroup 视图)
systemd-cgtop
9. 高级追踪工具
选型指引(BCC / bpftrace / ftrace / SystemTap 怎么选):四者都用于动态追踪,但适用场景不同。BCC 与 bpftrace 都基于 eBPF(主流内核 ≥ 4.9 可用,部分特性需更高版本),是新项目首选------BCC 提供大量开箱即用工具(biosnoop、tcptop、execsnoop...),适合直接查;bpftrace 用类 awk 的单行/脚本语法,适合临时写探针验证假设。ftrace 随内核自带、无需安装,走 tracefs(/sys/kernel/debug/tracing),适合无 eBPF 环境或需要内核函数级细节(调度、唤醒路径)。SystemTap 老牌但强依赖与运行内核完全匹配的 debuginfo,版本一变极易失败,仅老内核/特定发行版才考虑。结论:新环境一律优先 eBPF。
9.1 BCC(BPF Compiler Collection)
BCC(BPF Compiler Collection)是基于 eBPF 的高级工具集,提供现成的 Python/C 工具(如 execsnoop、biosnoop、tcplife),无需写内核模块即可在内核态高效采集数据。它是现代 Linux 动态追踪的事实标准,覆盖调度、I/O、网络、锁等几乎所有层面。
前置与权限:需内核 ≥ 4.9 且开启 CONFIG_BPF 等相关选项;安装 bcc-tools/bpfcc-tools 及内核头文件 linux-headers-$(uname -r);运行需 root。部分工具读取内核内部结构体,若内核版本与头文件不匹配可能报错,必要时装 linux-image-*-dbgsym。容器内需 --privileged 或 CAP_BPF/CAP_PERFMON。
bash
# 安装
yum install bcc-tools # RHEL/CentOS
apt install bpfcc-tools # Debian/Ubuntu
# 工具位于 /usr/share/bcc/tools/
# ===== CPU =====
execsnoop # 追踪新进程执行
runqlat 10 1 # 运行队列延迟直方图
runqlen 10 1 # 运行队列长度分布
cpudist 10 1 # 进程在 CPU 上的时间分布
offcputime 10 # 进程不在 CPU 上的时间及原因(阻塞分析)
profile 30 # CPU 采样(生成火焰图数据)
softirqs 10 1 # 软中断耗时
# ===== 内存 =====
memleak 10 # 内存泄漏追踪
oomkill # 追踪 OOM 事件
slabratetop 10 # slab 分配速率 top
pagein # 追踪页面换入
# ===== I/O =====
biolatency 10 1 # 块 I/O 延迟直方图
biosnoop # 每次块 I/O 详情
biostacks # I/O 发起栈
filetop -C 5 # 文件 I/O top
fileslower 10 # 慢文件操作
ext4dist 10 1 # ext4 操作延迟分布
xfsslower 10 # XFS 慢操作
zfsdist 10 1 # ZFS 延迟分布
cachestat 1 # 页缓存命中率
# ===== 网络 =====
tcptop 5 # TCP 连接流量 top
tcpconnect # 追踪新 TCP 连接
tcpaccept # 追踪 accept
tcpretrans # 追踪 TCP 重传
tcpdrop # 追踪 TCP 丢包
tcpconnlat # TCP 连接延迟
tcplife # TCP 连接生命周期
udpsnoop # UDP 包追踪
dnsreqs # DNS 请求追踪
# ===== 调度/锁 =====
offwaketime 10 # 阻塞时间 + 唤醒栈
funccount 'vfs_*' # 函数调用计数
funclatency 'vfs_read' 10 # 函数延迟分布
stackcount 'tcp_sendmsg' # 调用栈计数
memleak(bcc)前置与权限:依赖内核 eBPF 支持(基础功能 ≥ 4.9,部分特性需更高版本)以及与当前运行内核匹配的 kernel-headers;若栈回溯 / 符号不全,需安装对应 linux-image-*-dbgsym。运行需 root,或内核 ≥ 5.8 时授予 CAP_BPF / CAP_PERFMON。注意它不是内核自带的 kmemleak(后者用 /sys/kernel/debug/kmemleak,需编译开启)。
9.2 bpftrace(高级 eBPF 脚本)
bpftrace 是 eBPF 的单行/脚本前端,语法类似 awk/DTrace,适合临时编写自定义探针(kprobe/uprobe/tracepoint)快速验证假设,比 BCC 更轻量灵活。当你需要临时看某个内核函数被调用多少次/耗时多少时首选。
前置与权限:需内核 ≥ 4.x(建议 ≥ 4.9)、bpftrace 包及内核头文件;运行需 root,或 CAP_BPF + CAP_PERFMON(内核 5.8+)。生产环境短脚本验证即可,避免长期挂载高开销探针。
bash
# 安装
yum install bpftrace
apt install bpftrace
# 单行脚本示例
# 追踪 read 系统调用延迟
bpftrace -e 'tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
tracepoint:syscalls:sys_exit_read /@start[tid]/ { @ns = hist(nsecs - @start[tid]); delete(@start[tid]); }'
# 按进程统计系统调用次数
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
# 追踪 malloc 大小分布
bpftrace -e 'uprobe:/usr/lib64/libc.so.6:malloc { @bytes = hist(arg0); }'
# 追踪调度事件
bpftrace -e 'tracepoint:sched:sched_switch { @[args->prev_comm] = count(); }'
# 追踪 TCP 重传
bpftrace -e 'kprobe:tcp_retransmit_skb { printf("%s pid=%d\n", comm, pid); }'
# 生成火焰图
bpftrace -e 'profile:hz:99 { @[ustack] = count(); }' -c <command> -o out.bt
./FlameGraph/stackcollapse-bpftrace.pl out.bt > out.folded
./FlameGraph/flamegraph.pl out.folded > flame.svg
9.3 ftrace(内核函数追踪)
ftrace 是内核自带的函数追踪器,无需额外安装,通过 tracefs(/sys/kernel/debug/tracing)操作,可追踪指定函数、函数图、调度事件等。适合无 BCC/eBPF 环境下或需要内核函数级细节时(如调度、中断、唤醒路径)。
前置与权限:ftrace 随内核自带,无需额外安装,但需 tracefs 已挂载(通常 /sys/kernel/debug/tracing,依赖 debugfs 挂载,多数发行版默认开启);操作需 root。部分追踪器(如 function_graph)在低版本或特定内核配置下可能不可用,具体看 available_tracers。
bash
cd /sys/kernel/debug/tracing
# 查看可用追踪器
cat available_tracers
# hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop
# 函数追踪
echo function > current_tracer
echo vfs_read > set_ftrace_filter
echo 1 > tracing_on
cat trace_pipe
echo 0 > tracing_on
# 函数图追踪(显示调用树和耗时)
echo function_graph > current_tracer
echo vfs_write > set_graph_function
echo 1 > tracing_on
cat trace
echo 0 > tracing_on
# 事件追踪
echo 1 > events/sched/sched_switch/enable
echo 1 > events/irq/enable
cat trace_pipe
# 使用 trace-cmd 简化操作
trace-cmd record -e sched_switch -e sched_wakeup sleep 5
trace-cmd report
9.4 SystemTap
SystemTap 是较早的内核动态追踪框架,用脚本在内核任意位置埋点,功能强大但依赖内核 debuginfo、易因版本不匹配失败。如今多被 eBPF/BCC 替代,但在特定发行版/老内核上仍有价值。
前置与权限:需 systemtap + systemtap-runtime,以及与当前运行内核完全一致的 kernel-devel 与 kernel-debuginfo(debuginfo 通常体积大、且每个内核版本一份)。版本或配置不匹配是最常见的失败原因------这是它被 eBPF 取代的主因。
bash
# 安装
yum install systemtap systemtap-runtime
# 示例:追踪系统调用耗时
stap -e 'probe syscall.* { if (pid() == target()) printf("%s %d\n", name, gettimeofday_us()) }' -x <PID>
# 示例:I/O 调度延迟
stap /usr/share/systemtap/tapset/iostats.stp
9.5 火焰图全家桶
本节汇总各类火焰图(CPU、off-CPU、内存、I/O、eBPF 火焰图)的生成套路与适用场景,作为 3.4 与 9.x 的整合速查,帮助你针对不同类型瓶颈选对采样方式。
bash
# CPU 火焰图(用户态 + 内核态)
perf record -F 99 -ag sleep 30
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > cpu.svg
# 内存分配火焰图
perf record -e 'kmem:kmalloc' -ag sleep 30
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl --color=mem > mem.svg
# 阻塞火焰图(off-CPU)
# 使用 bcc offcputime
offcputime -df 30 > off.folded
./flamegraph.pl --color=io --title="Off-CPU" off.folded > offcpu.svg
# 差分火焰图
./difffolded.pl before.folded after.folded | ./flamegraph.pl > diff.svg
# Java 火焰图(async-profiler)
./profiler.sh -d 30 -f flame.html <PID>
# Go 火焰图
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30
9.6 容器环境性能分析
容器共享内核但受 cgroup 限制、有额外网络/存储层,排查思路与裸机不同(需进容器看 cgroup、用 cadvisor/node-exporter)。本节补充容器场景下的性能分析要点与常见坑。
bash
# Docker 容器资源查看
docker stats --no-stream
docker top <container>
# 进入容器 netns 分析网络
nsenter -t $(docker inspect --format '{{.State.Pid}}' <container>) -n ss -tanp
# cgroup 限制查看
docker inspect <container> | grep -i "memory\|cpu\|blkio"
# Kubernetes Pod
kubectl top pod -n <ns>
kubectl top node
kubectl describe pod <pod> | grep -A5 "Limits\|Requests"
# 容器内 perf(需要 privileged 或 SYS_ADMIN)
docker run --privileged -v /usr/src:/usr/src ...
容器排障常见坑:① 资源上限由 cgroup 决定------容器内 free/top 看到的是宿主机视角,真实限额要看 cgroup(/sys/fs/cgroup/.../memory.max、cpu.max、io.max,见 8.6);② 容器内跑 perf/eBPF 需 CAP_BPF/CAP_PERFMON 或 --privileged,否则报权限错误;③ 网络多一层 CNI(flannel/calico 等),抓包要在正确 netns(nsenter -t -n);④ 存储多一层 CSI/overlay,I/O 延迟要区分容器层与底层设备(结合 5.x)。k8s 场景优先用 kubectl top + Prometheus/cAdvisor 看真实用量。
10. 内核参数调优
10.1 网络调优
本节给出网络子系统的关键调优项(缓冲区、连接回收、offload、队列),按先测后调、一次一项原则应用,目标是降低延迟、提升吞吐。
bash
# /etc/sysctl.d/99-network-tuning.conf
# === TCP 缓冲区 ===
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# === 连接队列 ===
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535
# === TCP 连接复用 ===
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 2000000
net.ipv4.tcp_fin_timeout = 15
# === keepalive ===
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
# === 拥塞控制 ===
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq # bbr 需要 fq
# === 连接跟踪 ===
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
# === 端口范围 ===
net.ipv4.ip_local_port_range = 1024 65535
# 生效
sysctl -p /etc/sysctl.d/99-network-tuning.conf
10.2 内存调优
内存调优围绕回收策略(swappiness)、脏页阈值、透明大页(THP)、slab 等展开。本节列出常用可调参数及其对性能的影响,强调先建立基线再调整。
bash
# /etc/sysctl.d/99-memory-tuning.conf
# 减少 swap 使用
vm.swappiness = 1
# 脏页回写
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500
# 内存过量分配策略(1=总是允许,数据库常用)
vm.overcommit_memory = 1
# OOM 时杀进程而非 panic
vm.panic_on_oom = 0
# 透明大页(数据库通常关闭)
# 在 /etc/rc.local 或 systemd unit 中:
# echo never > /sys/kernel/mm/transparent_hugepage/enabled
# 页缓存回收倾向(值越大越倾向回收页缓存而非 slab)
vm.vfs_cache_pressure = 100
# min_free_kbytes:保留给内核的最小空闲内存
vm.min_free_kbytes = 1048576 # 1GB
sysctl -p /etc/sysctl.d/99-memory-tuning.conf
10.3 文件系统与 I/O 调优
I/O 调优涉及调度器选择、队列深度、文件系统挂载选项(noatime、日志模式)、readahead 等。本节给出针对不同负载(数据库/日志/大文件)的调优建议。
bash
# 文件描述符限制
fs.file-max = 2097152
fs.nr_open = 2097152
# inotify 限制(大量文件监控时需要)
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 8192
# AIO 限制
fs.aio-max-nr = 1048576
# 磁盘调度器(udev 规则)
# /etc/udev/rules.d/60-scheduler.rules
ACTION=="add|change", KERNEL=="sd[a-z]|nvme[0-9]*", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# 预读(顺序读多的场景增大)
blockdev --setra 4096 /dev/sda # 2MB 预读
10.4 CPU 与调度调优
CPU 调优包括调度器策略、CPU 亲和性(taskset/numactl)、节能模式(cpufreq)、中断均衡(irqbalance)等。本节给出提升计算密集型或延迟敏感型业务性能的常见手段。
bash
# CPU 频率策略
cpupower frequency-set -g performance
# 关闭 NUMA 自动平衡(数据库场景)
echo 0 > /proc/sys/kernel/numa_balancing
# 调度器参数
# 减少唤醒抢占延迟(对延迟敏感的应用)
echo 1000000 > /proc/sys/kernel/sched_min_granularity_ns
echo 1500000 > /proc/sys/kernel/sched_wakeup_granularity_ns
# 实时调度限制
echo -1 > /proc/sys/kernel/sched_rt_runtime_us # 允许 RT 任务无限运行(慎用)
# 使用 tuned 一键配置
tuned-adm list
tuned-adm profile throughput-performance # 吞吐优先
tuned-adm profile latency-performance # 延迟优先
tuned-adm profile network-latency # 网络低延迟
10.5 安全模块对性能的影响
SELinux、AppArmor 等安全模块在文件访问、exec 上做额外检查,会带来可测量的开销。本节说明如何评估其性能影响,以及在性能与安全的权衡中如何取舍。
bash
# SELinux 状态
getenforce
# 排查时可临时设为 permissive
setenforce 0
# auditd 规则过多会影响性能
auditctl -l # 查看规则
systemctl stop auditd # 临时关闭测试
# AppArmor
aa-status
11. 性能监控体系搭建
11.1 指标采集层
可观测性体系的基础是采集。本节表格梳理指标采集层的常见组件(node-exporter、telegraf、collectd 等)与采集对象,作为监控落地的前置知识。
| 工具 | 用途 | 采集方式 |
|---|---|---|
| node_exporter | 系统指标 | Prometheus pull |
| process_exporter | 进程级指标 | Prometheus pull |
| dcgm-exporter | GPU 指标 | Prometheus pull |
| smartctl_exporter | 磁盘 SMART | Prometheus pull |
| cAdvisor | 容器指标 | Prometheus pull |
| collectd | 通用系统指标 | push/pull |
| Telegraf | 通用采集 | push |
11.2 告警规则示例(Prometheus)
告警是监控闭环的一环。本节给出基于 Prometheus 的实用告警规则示例(CPU、内存、磁盘、网络阈值),可直接套用或改写。
yaml
groups:
- name: linux_performance
rules:
- alert: HighCPULoad
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
for: 10m
labels:
severity: warning
annotations:
summary: "CPU 使用率超过 90% 持续 10 分钟"
- alert: HighIOWait
expr: rate(node_cpu_seconds_total{mode="iowait"}[5m]) * 100 > 30
for: 5m
labels:
severity: warning
- alert: MemoryPressure
expr: rate(node_pressure_memory_waiting_seconds_total[5m]) > 0
for: 5m
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
for: 5m
labels:
severity: critical
- alert: HighNetworkErrors
expr: rate(node_network_receive_errs_total[5m]) + rate(node_network_transmit_errs_total[5m]) > 10
for: 5m
- alert: SwapUsageHigh
expr: (node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes) / node_memory_SwapTotal_bytes > 0.5
for: 10m
11.3 链路追踪与 APM
单看主机指标无法回答一次请求慢在哪个服务。链路追踪(tracing/APM,如 Jaeger、SkyWalking)还原请求调用链,本节说明其原理与在性能排查中的定位。
- 分布式追踪: Jaeger / Zipkin / SkyWalking / OpenTelemetry
- APM: Datadog / New Relic / Dynatrace / Elastic APM
- 日志: ELK (Elasticsearch + Logstash + Kibana) / Loki + Grafana
- Profiling 平台: Pyroscope / Parca / Datadog Continuous Profiler
11.4 Grafana 仪表盘推荐
Grafana 把采集到的指标可视化。本节推荐几套成熟的仪表盘模板(Node Exporter Full 等)与关键面板,帮助你快速搭建一眼看全系统的监控视图。
- Node Exporter Full (ID: 1860)
- Linux Performance Dashboard (ID: 14232)
- Process Exporter (ID: 249)
- NVIDIA DCGM (ID: 12239)
12. 经典案例实战
案例 1:CPU 100% 排查
现象: 某 Java 服务 CPU 持续 100%,响应超时。
bash
# Step 1: 确认是哪个进程
top -bn1 | head -15
# 发现 java 进程 PID=23456 占用 380% CPU
# Step 2: 找到最耗 CPU 的线程
top -H -p 23456
# 发现 TID=23478 占用 99% CPU
# Step 3: 转为 16 进制
printf "%x\n" 23478
# 输出: 5bac
# Step 4: jstack 查看线程栈
jstack 23456 | grep -A 30 "nid=0x5bac"
# 发现死循环代码位置
# Step 5: 确认根因
# 某正则表达式在特定输入下产生灾难性回溯 (ReDoS)
案例 2:内存泄漏导致 OOM
现象: 服务运行数天后被 OOM Killer 杀死。
bash
# Step 1: 确认 OOM
dmesg -T | grep -i "oom\|killed"
# [Mon Jan 15 03:22:11 2024] Out of memory: Kill process 12345 (myapp) score 850
# Step 2: 查看被杀时内存状态
dmesg -T | grep -B5 "killed process"
# 显示当时 MemFree 很低,某进程 RSS 异常大
# Step 3: 复盘------查看历史内存趋势
# 从 Prometheus/Grafana 查看该进程 RSS 曲线
# 发现每天增长约 500MB,不回收
# Step 4: 复现并定位
# 使用 jmap 分析堆
jmap -histo:live <PID> | head -30
jmap -dump:format=b,file=heap.hprof <PID>
# 使用 MAT (Memory Analyzer Tool) 分析
# 发现某 HashMap 只 put 不 remove,累积了 200 万对象
# Step 5: 修复
# 添加 TTL 过期机制或使用 WeakHashMap
案例 3:磁盘 I/O 延迟导致服务抖动
现象: 数据库每 5 分钟出现一次延迟尖刺。
bash
# Step 1: 确认 I/O 延迟
iostat -xz 1 60
# 每 300 秒出现一次 await > 500ms,%util 100%
# Step 2: 查看是什么进程在写
iotop -oPa
# 发现是 backup 脚本在做全量 tar
# Step 3: 确认时间吻合
crontab -l
# 0 */5 * * * /opt/scripts/full_backup.sh
# Step 4: 解决方案
# 方案 A: 调整备份时间到业务低峰
# 方案 B: 使用 ionice 降低优先级
ionice -c2 -n7 tar czf ...
# 方案 C: 限速
pv -L 50m /data/db | tar czf backup.tar.gz -
# 方案 D: 改用增量备份
案例 4:网络 TIME_WAIT 堆积
现象: 短连接服务报 "Cannot assign requested address"。
bash
# Step 1: 确认 TIME_WAIT 数量
ss -s
# TIME-WAIT: 280000
# Step 2: 查看端口范围
sysctl net.ipv4.ip_local_port_range
# 1024-65535,约 64000 个端口,但 TIME_WAIT 28 万远超
# Step 3: 确认是客户端还是服务端
ss -tan state time-wait | awk '{print $4}' | awk -F: '{print $NF}' | sort | uniq -c | sort -rn | head
# 大部分是本地端口(说明是本机作为客户端发起连接)
# Step 4: 解决
# 开启 tw_reuse(echo 写入 /proc 为临时生效、重启失效;持久化见下方 sysctl.d 配置)
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
# 扩大端口范围
echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range
# 减少 TIME_WAIT 时间
echo 15 > /proc/sys/net/ipv4/tcp_fin_timeout
# 根本方案:使用连接池
案例 5:上下文切换过高
现象: 系统 cs(上下文切换)达到 500 万次/秒,CPU %sy 30%。
bash
# Step 1: 确认
vmstat 1
# cs 列持续 5000000+
# Step 2: 定位进程
pidstat -w 1 5 | sort -k4 -rn | head -10
# 发现某进程 nvcswch/s(非自愿切换)极高
# Step 3: 查看线程数
ps -T -p <PID> | wc -l
# 3000 个线程
# Step 4: 分析原因
# 线程数远超 CPU 核数(64核),调度竞争激烈
# 且线程间频繁锁竞争导致非自愿切换
# Step 5: 解决
# 减少线程数,使用线程池
# 或减少锁粒度
案例 6:容器内 DNS 解析慢
现象: Kubernetes Pod 内 HTTP 请求偶尔超时 5 秒。
bash
# Step 1: 确认 DNS 延迟
time nslookup backend-service.default.svc.cluster.local
# 偶尔 5s+
# Step 2: 抓包分析
tcpdump -i eth0 -nn port 53
# 发现 DNS 查询发出后 5 秒才收到响应
# Step 3: 检查 conntrack
dmesg | grep conntrack
# "nf_conntrack: table full, dropping packet"
# 或 "insert_failed" 相关
# Step 4: 根因
# DNS 使用 UDP,conntrack 在高并发下 race condition 导致丢包
# 5 秒是 DNS 超时重试时间
# Step 5: 解决
# 方案 A: 增大 conntrack 表
sysctl -w net.netfilter.nf_conntrack_max=1048576
# 方案 B: Pod DNS 策略改为 NodeLocal DNSCache
# 方案 C: 应用使用 TCP DNS
# 方案 D: 使用 NodeLocal DNSCache (kube-dns 本地缓存)
案例 7:kswapd 导致 CPU 高
现象: 系统 %sy 高,perf 显示 kswapd0 占用大量 CPU。
bash
# Step 1: 确认
top
# kswapd0 占用 30% CPU
# Step 2: 查看内存压力
cat /proc/pressure/memory
# some avg10=45.00 ...
free -h
# available 很低
sar -B 1 5
# pgscan_kswapd 很高
# Step 3: 分析原因
# 某进程申请了大量内存,触发频繁页回收
# 或页缓存被大量使用,回收扫描开销大
# Step 4: 解决
# 增加物理内存
# 限制进程内存(cgroup)
# 调整 vm.min_free_kbytes 增大
# 减少不必要的页缓存(如使用 O_DIRECT)
13. 性能排查速查表
13.1 按症状速查
遇到具体症状(卡顿、OOM、丢包...)不知从何下手?本节按症状→可能原因→首选工具的速查表,帮你秒级定位排查入口。
| 症状 | 首先检查 | 命令 |
|---|---|---|
| 系统慢/卡 | 负载、CPU、I/O wait | uptime; vmstat 1; iostat -xz 1 |
| CPU 高 | 哪个进程、用户态还是内核态 | top; perf top; pidstat -u 1 |
| 内存不足 | 谁在用、是否泄漏 | free -h; ps aux --sort=-%mem; smem |
| 磁盘慢 | IOPS、延迟、哪个进程 | iostat -xz 1; iotop; blktrace |
| 网络慢 | 丢包、重传、带宽 | sar -n DEV; ss -s; nstat -az |
| 进程 D 状态 | I/O 卡住、NFS | ps aux; cat /proc/<pid>/stack |
| OOM Kill | 谁被杀、内存去哪了 | dmesg; /proc/meminfo; cgroup |
| 连接超时 | 连接队列、TIME_WAIT | ss -s; nstat; tcpdump |
| 中断风暴 | 哪个中断、是否集中 | /proc/interrupts; mpstat -P ALL |
排查决策流(钻取思路):先跑 60 秒法则(1.3)建立全局画像 → 用 USE 方法(1.1)定位瓶颈资源(CPU/内存/磁盘/网络)→ 进对应章节选概览工具确认现象 → 用进程级工具锁定罪魁进程 → 用深度追踪工具(perf/eBPF/strace)定位代码/内核级根因 → 调优(第 10 章)前后务必用基准(13.3)对比验证。症状→工具映射见上表,工具层级关系见 13.2。
13.2 工具矩阵
一张表汇总全文工具:用途、层级、是否需安装、适用场景。作为该用哪个工具的总索引。
| 资源 | 概览 | 进程级 | 深度追踪 |
|---|---|---|---|
| CPU | top, mpstat, vmstat | pidstat -u, perf top | perf record, flamegraph, bcc profile |
| 内存 | free, /proc/meminfo | pmap, smem, /proc/pid/smaps | memleak, bpftrace, kmemleak |
| 磁盘 | iostat, /proc/diskstats | iotop, pidstat -d | blktrace, biosnoop, biolatency |
| 网络 | sar -n, ip -s, ss | nethogs, tcptop | tcpretrans, tcpdump, bpftrace |
| 调度 | vmstat(r/b), runqlat | pidstat -w | perf sched, offcputime |
| 文件 | df, mount | lsof, /proc/pid/fd | fileslower, strace, opensnoop |
13.3 性能测试基准命令
调优前后必须用基准测试验证效果。本节列出常用基准命令(cpu: sysbench、mem: stream、io: fio、net: iperf3),统一测试口径以便对比。
bash
# CPU 基准
sysbench cpu --threads=64 --time=30 run
# 内存带宽
sysbench memory --threads=64 --time=30 run
stream # STREAM benchmark
# 磁盘 IOPS
fio --name=randread --ioengine=libaio --direct=1 --bs=4k \
--iodepth=32 --rw=randread --size=1G --runtime=60 \
--filename=/dev/sdb --group_reporting
fio --name=seqwrite --ioengine=libaio --direct=1 --bs=128k \
--iodepth=16 --rw=write --size=4G --runtime=60 \
--filename=/data/testfile --group_reporting
# 网络带宽
iperf3 -c server -t 30 -P 4
# 网络延迟
sockperf ping-pong -i server -t 30
13.4 常用 sysctl 查看
sysctl 是内核运行时参数的入口。本节汇总最常用的一批 sysctl 查看命令(与 6.7/10.x 呼应),便于快速核对当前内核配置。
bash
# 一键查看关键参数
sysctl -a 2>/dev/null | grep -E "^(net\.core\.(rmem|wmem|somaxconn|netdev_max)|net\.ipv4\.tcp_(rmem|wmem|max_syn|tw_reuse|fin_timeout|keepalive|congestion)|vm\.(swappiness|dirty_ratio|dirty_background|overcommit|min_free)|fs\.(file-max|nr_open)|kernel\.(sched|numa|hung_task|softlockup))"
13.5 性能分析思维导图
以一张思维导图收束全文,把方法论 → 工具 → 调优 → 监控串成体系,便于记忆与复盘。
性能问题
├── 是变慢了还是不可用了?
│ ├── 变慢 → 找瓶颈资源 → USE 方法
│ └── 不可用 → dmesg / journalctl / OOM / 硬件故障
├── 是持续还是间歇?
│ ├── 持续 → 资源不足 / 配置问题
│ └── 间歇 → cron / GC / 备份 / 流量尖峰
├── 影响范围?
│ ├── 单进程 → 应用层分析
│ ├── 单容器 → cgroup 限制 / 邻居干扰
│ └── 全机 → 系统级资源竞争
└── 最近有什么变更?
├── 部署 / 配置变更 → 回滚验证
├── 内核升级 → 对比
└── 硬件变更 → 驱动 / 固件
附录 A:推荐学习资源
| 资源 | 说明 |
|---|---|
| 《Systems Performance》- Brendan Gregg | 系统性能圣经 |
| 《BPF Performance Tools》- Brendan Gregg | eBPF 性能分析 |
| 《Linux Performance Optimization》- Christ |