惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
Engineering at Meta
Engineering at Meta
量子位
A
About on SuperTechFans
阮一峰的网络日志
阮一峰的网络日志
Recent Announcements
Recent Announcements
博客园 - 司徒正美
V
Visual Studio Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
F
Fortinet All Blogs
Martin Fowler
Martin Fowler
腾讯CDC
Jina AI
Jina AI
C
Check Point Blog
H
Help Net Security
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
爱范儿
爱范儿
I
InfoQ

暗无天日

读:AI Agent 安全日志——从可见性与隐私的两难说起 - 暗无天日 AI写作的语言指纹——如何让文字不那么像机器 - 暗无天日 读:50 条 Claude Code 技巧——一个工程经理的六个月使用心得 读:AI 辅助开发为什么让 E2E 测试更有价值 - 暗无天日 读:在Emacs中使用Claude Code(Spacemacs适配版) - 暗无天日 Claude Code 背后的工程哲学——读 Agent Harness Engineering 读:Agent Harness Engineering——AI 智能体不只是模型,还有套件 - 暗无天日 browser-harness:让 AI 直接接管你的浏览器 - 暗无天日 读:Security-First CI/CD —— DevSecOps 自动化实践指南 TIL: 数字小键盘的小数点陷阱与行内算术求值 - 暗无天日 读:Immutability 不是万能药,它是一种权衡 - 暗无天日 Conducty:给 Claude Code 加上项目记忆和并行执行能力 - 暗无天日 读 — GitHub Trending 里的 Claude Code 技能包 读 — Prompt Caching 省钱指南 TIL: Emacs 中那些跟鼠标配合的冷门快捷键 - 暗无天日 读:Anvil——把 Emacs 变成 AI 的工具服务器 读:Emacs 代码折叠终极指南 - 暗无天日 读:Clojure 搭车客指南 - 暗无天日 git推送失败后恢复仓库损坏的完整记录 - 暗无天日 多智能体系统的两个有效模式——以及对 Claude Code 用户的启示 - 暗无天日 用 Org Babel 写 Literate 博文:扩展执行 + 定制导出 proced:Emacs 内置的进程查看器 - 暗无天日 从 proced 定制中学到的 Elisp 模式 读:让 Emacs proced 在 macOS 上显示 CPU 和内存 异步编程的函数着色税 - 暗无天日 链式调用的代价:JavaScript 和 Clojure 的共同教训 - 暗无天日 hyperfine:命令行基准测试工具 - 暗无天日 管道中的变量去哪了?——子 shell 作用域陷阱 - 暗无天日 开源包装器的信任陷阱:四个危险信号 - 暗无天日 程序员愿意为 AI 写文档,却不愿为同事写 - 暗无天日
读:sysstat 诊断链——从 sar 到 pidstat 的排查路径
2026-05-06 · via 暗无天日

引子

系统变慢的时候,大多数人的第一反应是敲 top 。这没问题。 top 给你一个即时快照:谁在用 CPU、内存还剩多少、load average 大概什么水平。

top 有两个盲区。第一,它只看当下。凌晨 3 点发生的性能问题,你早上 9 点坐到工位时 top 已经什么都看不到了。第二, top 能看到每个进程用了多少 CPU,但很难同时看清三件事:哪个核在忙、忙的是哪个线程、当时有多少进程在排队等 CPU。这三个信息分散在不同的视图里,手工拼凑太慢。

sysstat 套件补上了这两个盲区。它包含四个核心工具: mpstatpidstatsariostat 。TecMint 上有篇 文章 系统介绍了它们的用法。本文不逐条翻译那 20 个示例,改用一条真实的排查链路把它们串起来,从发现异常到定位根因。

安装和启用

sysstat 大部分发行版默认不装:

sudo apt install sysstat
sudo dnf install sysstat
sudo pacman -S sysstat

装完确认版本:

mpstat -V
sysstat 版本 12.7.9
(C) Sebastien Godard (sysstat <at> orange.fr)

Ubuntu 用户特别注意 :安装后数据收集默认是关闭的。需要编辑 /etc/default/sysstat ,把 ENABLED="false" 改成 ENABLED="true" 。否则 sar 不会自动记录历史数据,而这是整篇文章最有价值的功能,漏掉等于白装。

诊断第一环:sar -u 回看历史

场景:早上到工位,同事说昨晚系统很卡。你可以通过 sar 回顾当时的系统指标。

sar 通过 cron 定时采集系统指标,存成二进制日志。你可以指定任意时间段回放。

配好 cron 自动收集(这一步是整篇文章最重要的事):

*/10 * * * * /usr/lib/sysstat/sa1 1 1   53 23 * * * /usr/lib/sysstat/sa2 -A

sa1 / sa2 的路径因发行版不同:

  • Debian/Ubuntu: /usr/lib/sysstat/sa1
  • RHEL/Rocky: /usr/lib64/sa/sa1
  • Arch Linux: /usr/lib/sa/sa1

Debian/Ubuntu 下 which sa1 能找到,Arch 下 sa1/usr/lib/sa/ 而不在 PATH 里,用 ls /usr/lib/sa/sa1 确认。确认路径后替换 cron 条目中的对应位置。

确认数据在积累:

ls -lh /var/log/sysstat/

回放历史 CPU 数据:

sar -u -f /var/log/sysstat/sa$(date +%d)

sar -u 的输出项跟 mpstat 类似( %=user%=system%=iowait 等),关键在于它能告诉你问题发生的具体时段。知道时间点之后,后面的工具才有用武之地。

如果你不需要回放历史、只想看当前的连续采样,让 sar 交互式跑几轮:

sar -u 2 5

2 5 的意思是每 2 秒采一次,采 5 次。最后会有一行 Average: 汇总。

诊断第二环:mpstat -P ALL 定位热核

sar 告诉你"昨晚 10 点到 11 点 CPU 偏高",接下来要回答"是所有核都忙,还是单核被打满"。

mpstat 报告逐核 CPU 使用率。不加参数时只给一个全局汇总:

mpstat

加了 -P ALL 之后,每个 CPU 核心一行:

mpstat -P ALL 2 5

这时候你可能看到类似这样的输出:

CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
all   19.87    0.01    3.02    0.88    0.00    0.08    0.00    0.00    0.00   76.14
  0   72.14    0.00    5.22    0.00    0.00    0.00    0.00    0.00    0.00   22.64
  1   14.32    0.03    2.88    0.52    0.00    0.08    0.00    0.00    0.00   82.17
  2   10.44    0.02    2.71    1.98    0.00    0.12    0.00    0.00    0.00   84.73
  3    8.11    0.01    1.75    3.52    0.00    0.09    0.00    0.00    0.00   86.52

CPU 0 的用户态使用率 72%,其他三个核几乎空闲。这是经典的单线程进程打满单核的特征。

几个值得注意的列:

  • %iowait :持续超过 10-15% 意味着系统在等磁盘读写。这时候加 CPU 没用,问题在磁盘。
  • %soft :软件中断占比。网络数据包到达后,内核需要软中断来处理协议栈(TCP/IP 拆包、路由等)。 %=soft 高通常意味着网络流量打满了,CPU 在忙于收发包而非跑你的应用程序。

如果怀疑中断不均衡(比如网卡中断全压在 CPU 0 上),用 -I ALL 看中断分布:

mpstat -I ALL

NET_RX/s 集中在某个核上、同时该核 %soft 偏高,说明网卡中断亲和性(网卡硬中断固定发给某个 CPU 处理)需要调整。要么用 irqbalance 服务自动分配中断到多个核,要么手动写 /proc/irq/<编号>/smp_affinity 把特定中断号绑到其他 CPU 上。

诊断第三环:pidstat 找到凶手进程

mpstat 告诉你"CPU 0 被打满了",下一个问题自然是"谁打的"。

pidstat 报告进程级别的 CPU、内存、I/O 使用率。不加参数列出所有活跃进程:

pidstat
UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
1000     2841   18.44    0.88    0.00    0.00   19.32     0  python3
1000     3102    0.12    0.04    0.00    0.00    0.16     2  nginx

CPU 列显示该进程最后跑在哪个核上,和 mpstat 的热核直接对应。

%=wait 是一个容易被忽略但非常重要的列(sysstat 11.5 引入):它表示进程就绪后在运行队列里等待 CPU 调度的时间。这项持续非零,意味着 CPU 不够用,就绪队列里堆了太多工作,不是进程本身慢。

有了可疑的 PID,用 -t 展开线程看细节:

pidstat -t -p 2841 2 3
TGID       TID    %usr %system  %guest   %wait    %CPU   CPU  Command
2841         -   18.00    0.88    0.00    0.00   18.88     0  python3
   -      2841   17.50    0.50    0.00    0.00   18.00     0  |__python3
   -      2844    0.50    0.38    0.00    0.00    0.88     1  |__ThreadPool-1

输出里主线程( python3 )占 18.00% CPU,线程池( ThreadPool-1 )仅 0.88%。而且两行都是 %wait 为零、 %=usr 占大头,说明线程没有在等 CPU,也没有在等 I/O,就是在做纯用户态计算。这样可以定位:CPU 消耗集中在这个进程的主线程上,检查主线程的执行路径就能找到耗 CPU 的代码。

如果进程名已知,用 -G 直接过滤,不用在进程列表里找:

pidstat -G nginx 2

诊断第四环(条件):追 I/O 和内存

如果 mpstat 发现 %iowait 偏高

pidstat -d 报告每个进程的磁盘读写量:

pidstat -d 2
PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
2841    412.00     88.00      4.00      12  python3

iodelay 列是非零值时,该进程就是在等 I/O。和 mpstat 里高 %iowait 互相印证,凶手就确认了。

sar -d 则从块设备层面看延迟:

sar -d 1 3
  • await :请求从排队到完成的总时间
  • aqu-sz :平均队列深度

await 持续高但 aqu-sz 接近零,说明磁盘本身就慢(比如机械盘随机读写)。 await 高且 aqu-sz 也在 1 以上,则是排队已形成,磁盘处理不过来。

注意:旧文档常提的 svctm 字段在新内核中已废弃,sar 输出里该列不再可靠,不要依赖它做判断。

如果怀疑内存压力

sar -r 报告内存使用情况。重点看 %=commit 列:

sar -r 1 3

%=commit 是内核承诺给进程的虚拟内存总量相对于物理内存 + swap 的比例。超过 100%,意味着所有进程同时要求兑现承诺的话,OOM killer 就会出动。

pidstat -r 则从进程视角看内存:

pidstat -r -p 2841 2 3

VSZ 是虚拟内存, RSS 是物理内存。重点看 majflt/s (主缺页)列:主缺页意味着内核必须从磁盘捞回被换出的页面,持续非零说明系统在颠簸(thrashing),进程响应速度会明显下降。次缺页( minflt/s )不贵,只是内核在物理内存里映射页面而已。

贯穿始终:sar 历史 + sadf 导出

sar 真正的价值不是交互式采样(这个 mpstat 和 pidstat 也能做),而是自动收集的历史数据。配好 cron 后,任何发生过的性能问题你都可以事后回放。

回放历史数据时,用 sadf -d 导出为 CSV 格式,可以在表格里画图、和同事分享:

sadf -d /var/log/sysstat/sa$(date +%d) -- -n DEV | grep -v lo

排查链总结

整个过程是一条线:

  1. sar -u 回看历史,确定问题时段
  2. mpstat -P ALL 定位热核,看是均匀分布还是单核瓶颈
  3. pidstat 找到具体进程, -t 展开线程定位
  4. 如果 %iowait 高, pidstat -d + sar -d 追磁盘;如果怀疑内存, sar -r + pidstat -r 查换页

最值得现在做的事:把 sar 的 cron 采集配好,攒一周基线数据。有问题发生时,有历史数据和没有历史数据,排查时间是分钟级和小时级的差别。