1. vmstat 到底是什么,为什么你要用它
搞 Linux 运维和性能调优的朋友,一定绕不开 vmstat 这个名字。它全称是 Virtual Memory Statistics,直译过来就是虚拟内存统计,是 Linux 系统自带的一个性能监控命令。我第一次认真研究它,是在一次线上服务响应变慢的排查中:top 看 CPU 并不高,free 看内存还有不少余量,但服务就是卡得要命,后来是 vmstat 一出手,直接让我看到了 swap 的 si 和 so 数值异常跳动,才锁定了内存回收导致的性能抖动。从那以后,vmstat 就成了我诊断性能问题时的第一板斧。
vmstat 能干什么?简单说,它能在一条命令里同时展示进程状态、内存分布、Swap 换入换出、I/O 读写、系统中断、上下文切换以及 CPU 时间分配情况。很多人习惯把它当作一个"看内存"的命令,其实它是一台机器整体运行状态的一个缩影。它的输出格式非常紧凑,不花哨,没有彩色高亮,但恰恰是这个特点,让它在无人值守的脚本采集、ssh 登录到远程机器快速摸底、以及高负载现场不带任何图形界面时,成为最可靠的工具。注意,它显示的是系统的整体全局统计,而不是某个进程的个体数据,这一点和 top、pidstat 的定位是有区别的。
我们说回适用人群:如果你是刚入门的运维、正在准备系统管理员相关面试的工程师,或者负责业务应用部署的研发同学,vmstat 都能帮你建立"系统层性能分析"的基本框架。它的入门门槛极低,任何一台 Linux 发行版都预装了,不需要额外安装任何东西,也不需要申请什么权限,普通用户就能执行,最多看到的信息没有 root 那么全面而已。所以这篇文章我会从字段含义、常用参数、实战场景到误区规避,把它掰开揉碎讲清楚,让读完的你能够在真实故障中直接调用这套方法论。
2. 输出结果字段逐个拆解:不止是"看内存"
2.1 procs 与 memory 区域:一眼看出任务排队和内存余量
vmstat 不带任何参数执行,它会输出一次平均值,也就是自系统启动以来的平均统计。这里如果你想看当前实时状态,一定要带间隔和次数,比如vmstat 1 5表示每隔 1 秒输出一次,共输出 5 次。这个我们后面细说,先来看输出的各个字段。
第一行是 procs 区域,包含 r 和 b 两个字段。r 表示当前处于可运行状态(正在占用 CPU 或等待 CPU 时间片)的进程数量,说白了就是"排队等着用 CPU 的进程数"。这个值在判断 CPU 是否饱和时非常关键。如果你的机器是 2 核 CPU,r 值长期高于 4,那基本可以说 CPU 资源是紧张的,进程在门口排队。b 表示不可中断睡眠状态的进程数,这类进程通常是正在等待 I/O 资源,比如读写磁盘卡住了。b 值持续偏高,往往意味着 I/O 子系统有问题,或者是存储设备性能跟不上。
接下来是 memory 区域。swpd 显示被交换到 swap 分区或 swap 文件的内存总量,单位是 KB。这里要留个心眼,swpd 数值大不代表当前有问题,很多服务器 swap 分区设置得很大,偶发写入后数值也一直居高不下,只要后续不持续增大,就不必太焦虑。free 是当前物理内存中完全空闲的容量,这个数值在很多人的直觉里认为越大越好,但实际上在现代 Linux 的内存管理机制下,free 小一点反而是内存被充分利用的表现,因为系统会倾向于把空闲内存用作缓存。buff 和 cache 是容易混淆的一组概念。buff 是块设备读写缓冲区,用于缓冲对块设备的直接读写操作;cache 则是文件系统页缓存,用于缓存文件内容,后续读取相同文件可以直接命中内存,大幅提升性能。
在较新的内核版本里,你可以通过vmstat -a查看 Active 和 Inactive 内存量的更精细划分。active 表示正在被使用的内存页,inactive 表示不活跃、可以被回收的内存页。这个信息在判断"内存还有多少压榨空间"时很有参考价值:如果 inactive 内存量非常充足,系统内存紧张时是可以先回收这些页的,不一定非要立刻触发 swap。
2.2 swap 与 io 区域:真正的性能杀手藏在这里
swap 区域的两项指标,si 和 so,是判断内存是否吃紧的一座"金矿"。si(swap in)表示每秒从 swap 换入到物理内存的数据量,so(swap out)表示每秒从物理内存换出到 swap 的数据量,单位都是 KB/s。这两个值在绝大多数健康运行的 Linux 服务器上应该是接近 0 的。如果你在连续多次采样中发现 si/so 持续非零,哪怕数值不大,都说明物理内存已经不够用了,系统正在进行频繁的内存页换入换出。
为什么说 swap 抖动是"性能杀手"?可以类比一下:物理内存的访问速度是纳秒级,而磁盘的访问速度是毫秒级,几个数量级的差距。一旦内存页被换到磁盘上,CPU 再访问这个数据时要等磁盘 I/O 完成,性能会断崖式下跌。我曾经遇到过一台数据库服务器,CPU 和磁盘 I/O 指标看起来都还行,但服务就是频繁超时,最后就是 si/so 一直在跳动,每秒几百 KB 的换入换出,导致随机读写放大,整体延迟暴增。
io 区域的 bi 和 bo,分别表示每秒从块设备接收到的块数(读入)和每秒发送到块设备的块数(写入),单位也是 KB。注意这里统计的是块设备的 I/O,不仅仅是磁盘,还包括所有块设备。这两个字段需要结合磁盘性能基线来看。如果 bi 长期非常高,说明有很多读操作是穿透了文件缓存,直接落到了磁盘,这时就要检查缓存命中率是否过低,或者业务是否有大量随机读。bo 很高则意味着写压力大,或者可能是日志刷盘太频繁。
这里还必须强调一个内核版本的差异。在 Linux 2.6 之前的版本中,si 和 so 的单位是"页/秒"而不是 KB。虽然现在主流发行版内核都远新于 2.6,但一些老系统或者面试题里还可能提到这种旧表述,遇到时不要懵,知道它背后代表同样的含义即可。
2.3 system 与 cpu 区域:中断频率和 CPU 时间账单
system 区域包含 in 和 cs 两个字段。in 表示每秒设备中断数,包含时钟中断;cs 表示每秒上下文切换次数。这两个数值能反映系统的"忙碌程度"。在高并发的业务场景中,上下文切换次数每秒数万次其实是正常的,但如果你发现 cs 出现异常飙升,比如从几百跳到几万,同时 CPU 的 sy 占比也同步上升,就要考虑是不是进程或线程数量过多,或者是线程频繁在核间迁移,这时候就需要结合 pidstat 去看具体是哪些进程在制造上下文切换。
cpu 区域是我们最直观关注的一部分,也是面试时高频考察的知识点。us 表示用户态时间占比,即执行应用程序代码所占的 CPU 时间;sy 表示内核态时间占比,即操作系统内核(如系统调用、中断处理、驱动代码)所占的 CPU 时间。id 是空闲时间占比,wa 是等待 I/O 完成的时间占比。最后一个 st 是 steal time,仅在虚拟化环境中有意义,表示当前宿主机上其他虚拟机抢占 CPU 的时间占比,如果 st 太高,需要考虑是不是宿主机资源超售了。
很多人看 CPU 指标只看 us 高不高,这是远远不够的。us 和 sy 加起来是 CPU 的忙碌比例,但两者比例关系也有讲究。如果 sy 持续超过 us,甚至 sys 占比接近 50% 以上,那很可能你的程序在做大量的系统调用,比如频繁读写小文件、频繁创建销毁线程,或者存在锁竞争。id 高但 r 队列还在排队,说明进程都在等 CPU 之外的其他资源;wa 高往往是磁盘慢的直接信号。
3. 用法进阶:参数组合和输出模式挖掘
3.1 基础参数:interval 和 count 怎么用才算科学
vmstat 最核心的用法就是带间隔采样。vmstat 1会每隔 1 秒输出一行,从不自动停止,直到你用 Ctrl+C 终止。vmstat 1 5则是输出 5 次后自动退出,适合在脚本里采集固定时长的数据。我强烈建议你把第一行和后续行区别看待:第一行是开机以来的平均值,后面每一行才是真正反映当前实时状况的数据。所以如果你只执行一次vmstat,看到的其实是个"平均脸",很容易掩盖突发问题。要判断实时状态,最少也要连续采样两次以上。
采样的间隔选择也有讲究。如果机器负载明显异常,可以先用 1 秒间隔快速采样个 5 到 10 次,抓住尖峰。如果是在业务平稳期做基线采集,建议用vmstat 3 20或vmstat 5 12,用 1 分钟到 3 分钟的数据来做判断,尽量避免只看一瞬间的状态。另外,采样时最好避开整点或定时任务执行的时间窗口,否则容易采集到 cron 任务集中运行造成的虚假高峰。
在实际操作中,我习惯把 vmstat 输出重定向到文件后台运行,比如nohup vmstat 2 300 > /tmp/vmstat_$(date +%Y%m%d_%H%M%S).log &,这样既能持续采集,也不会因为终端断开而中断。事后用 grep、awk 或者直接拖到 Excel 里做简单统计分析都可以。
3.2 容易被忽略的其他参数:-a、-m、-d、-s
除了常规的采样参数,vmstat 还有几个实力派参数值得专门拿出来说。第一个是-a,它把内存区域显示切换为 Active 和 Inactive 内存的展示,让你看到哪些内存页是活跃的,哪些是懒散的。这个数据在做内存回收分析时很有用,因为系统回收内存时优先回收 inactive 页,如果 inactive 很少而内存又不足,就更容易触发 swap。
第二个是-m,这个参数显示内核 slab 信息,也就是内核对象缓存区的使用情况,单位是字节。如果你怀疑某个内核对象(比如 dentry、inode)占用内存过多,vmstat -m能直接看到每种缓存对象的数量、活跃对象数和总大小。这个参数在日常排查中可能使用频率不高,但遇到内核内存泄漏的表现形态时,它是不可多得的利器。
第三个是-d,它输出磁盘 I/O 的详细统计,包括每块磁盘的读次数、合并次数、扇区数、读耗时、写次数、写耗时等。要知道 vmstat 在整体视图里只给了 bi/bo 两个总括指标,而内核里真实的 I/O 情况其实是每个磁盘各算各的,-d正是把这些细节摊开给你看。当你怀疑是某块盘异常拖累整体性能时,用这个参数做一个针对性观察,比直接上 iostat 更快捷,因为在同一套工具链里解决问题,心智负担更小。
第四个是-s,它把各类事件从开机到现在的统计数值汇总展示,包括总内存、已用内存、活跃内存、不活跃内存、换入换出总量、磁盘读写总量、上下文切换总量、中断总量等。这个参数非常适合快速查看一台机器的"生命周期统计",做容量规划或者对比服务器配置时非常高效。还有一个-w参数,可以把输出改为宽格式,字段之间用空格拉开,视觉上更利于直接阅读,但内容没有本质区别。-t则会在每行添加时间戳,采集长时间数据后回看时能直接定位到某个时刻。
4. 实战案例:用 vmstat 定位三次真实故障
4.1 案例一:swap 频繁抖动,内存到哪儿去了
有一回我负责的一台应用服务器,业务反馈接口延迟从 20ms 涨到了 800ms,重启后也只能恢复一小会儿。我登录机器后先执行free -h,发现还有 2GB 可用内存,当时第一反应是不是网络问题。但多留了个心眼,用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 1 0 524288 198000 12340 1065000 1260 3400 1680 9600 15800 72000 22 15 35 28 0 0 1 524388 185000 12340 1065000 1180 3200 1520 8800 15600 71000 23 16 32 29 0这里的 swpd 高达 512MB,并且 si/so 都在持续跳动,说明系统一边在把页换出去,一边又把旧数据换进来,形成了颠簸。虽然 free 还有 200MB 左右,但结合 cache 也没有明显增长,说明内存确实处在紧缺状态。进一步排查后,发现是应用里一个内存缓存的 maxSize 设置过大,JVM 堆内存超配,加上物理机本身只有 4GB 内存,才导致系统不得不依赖 swap 硬撑。
这个案例的经验教训很有价值:free 看到有几百 MB 余量不能说明任何问题,也许那几百 MB 正是 swap 换入换出之间的"中场休息"。内存指标一定要结合 si/so 的实时走势来看,只要 si 或 so 连续多行非零,就值得立刻警惕,不要等到内存耗尽才去查。
4.2 案例二:wa 居高不下,CPU 在等谁
另一次是数据库备份任务执行期间,监控面板提示 CPU 使用率爆满。我打开 vmstat 采样,看到了下面这类输出:
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 4 0 45000 12000 280000 0 0 45000 11800 32000 78000 4 8 10 78 0 2 5 0 39000 12000 285000 0 0 52000 12000 33000 82000 3 7 8 82 0us 和 sy 并不高,只有百分之几,但 wa 高达 78% 至 82%,而且 b 列(阻塞进程数)也非常高。这个画面其实是典型的 I/O 瓶颈:CPU 本身不是满负荷,但进程想要运行就必须等待磁盘数据。我马上意识到备份任务正在大量读取数据,但是磁盘读速度跟不上应用的处理速度。当时通过vmstat -d查看具体是哪块设备在读,发现确实是备份任务使用的数据卷。后续通过调整备份任务的并发线程数、将备份时间窗口错峰,wa 明显下降。
初学者容易把"CPU 高"和"系统慢"直接画等号,但这个案例告诉我们,CPU 时间里有很大一部分可能是"干等"。wa 高时,盲目加 CPU 核心数完全没有意义,正确的方向是提升存储性能或减少无效 I/O。
4.3 案例三:r 队列持续大于核数,CPU 饱和的判定
还有一次是处理一个 Java 网关服务,业务高峰期时大量请求报超时。我看 top 的时候发现 %Cpu(s) 里 us 已经超过 90%,这看起来是 CPU 跑满了。但我进一步用 vmstat 做精细判断,重点关注 r 列:
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 8 0 0 120000 15000 450000 0 0 110 120 46000 98000 91 7 1 1 0 10 0 0 110000 15000 451000 0 0 105 110 46000 101000 90 8 1 1 0这台机器是 8 核的,但 r 列始终在 8 到 10 之间徘徊,而且持续不下。这说明进程队列已经饱和,新到的请求无法在第一时间获得 CPU,只能排队等待。id 也几乎为 0。这种状态意味着 CPU 资源确实是瓶颈,再多的调优也很难靠软件层面解决,最后我们的处理方案是扩容和拆分服务,把一部分流量分流到其他节点。
这里我想强调一个判断 CPU 是否饱和的口诀:当 r 列的值长期大于 CPU 核心数时,就意味着进程在排队等待 CPU;当 id 接近 0 但 r 还不高时,说明 CPU 在忙一些别的事,比如等待 I/O 或者被虚拟化抢占。判断一个机器是否 CPU 瓶颈,r 比 us 更有说服力,因为它直接反映了排队情况。
5. 配合其他命令交叉验证与常见误区
5.1 与 free、top、sar、pidstat 的交叉定位
vmstat 擅长给你"是什么"的答案,但"为什么"往往需要借助其他工具深挖。最经典的组合拳是先跑vmstat 1 5看整体状态,再根据可疑指标切换工具。如果发现 si/so 偏高,紧接着执行top看是哪个进程的 RES 占用异常,或者用ps aux --sort=-rss | head直接按内存排序。如果发现 wa 偏高,继续用iostat -x 1查看每块磁盘的 %util 和 await,确认是哪块盘在拖后腿。如果发现 cs 异常高,用pidstat -w 1查看各进程的 cswch/s(自愿上下文切换)和 nvcswch/s(非自愿上下文切换),揪出元凶。
sar 是另一个值得长期采集的工具,它的历史数据回放能力特别适合做性能复盘。vmstat 采集的数据如果只是临时看一眼,当时没保存,事后想对比就无从下手。所以建议在生产环境上把 sar 数据采集常驻打开,遇到问题时再看历史趋势。而 vmstat 更像是医院的"初诊号脉",先快速定位大概方向,再用其他工具做"专科检查"。几个工具分工各有侧重,但 vmstat 永远是第一站,因为它的信息密度和响应速度都极高。
5.2 五个常见误判与避坑经验
第一个坑是只看单次结果就下结论。单次 vmstat 反映的是此刻或开机以来的平均状态,一个瞬时尖峰可能完全不代表整体健康度。正确操作是至少连续采样 3 到 5 次,观察趋势而不是孤立点。第二个坑是混淆 buff 和 cache。虽然现在很多监控系统把它们合并成"缓存"展示,但它们的回收优先级和触发机制不同,在做内存调优时最好分清。第三个坑是在容器里查看 vmstat。容器内的 /proc 在很多情况下是宿主机视角,vmstat 显示的是宿主机的全局数据,而不是容器的资源使用情况,这一点尤其在 Kubernetes 环境里容易让人误判,需要依赖 cgroup 指标或 kubectl top 等工具。
第四个坑是忽视了 st 字段。在云服务器或虚拟机环境里,st 偏高是供应商资源超售的强信号,如果你看到 us 不高、id 也不高但 st 很高,那是隔壁邻居在抢你的 CPU。这时在操作系统内部任何调优都可能收效甚微,应该考虑迁移实例或联系服务商。第五个坑是死记硬背具体数值的经验法则。比如有人总结"si 大于 1000 就是严重内存不足",但一台大内存机器和一台小内存机器对 swap 的容忍度完全不同。数值的意义要结合机器的总内存、业务类型和基线来看,最科学的方法是长期采集形成自己环境的基线,再拿当前数据与基线对比。
对这些坑有了认知,vmstat 用起来才算真正得心应手。它是一个工具,但它背后是一套系统全局性能分析的方法论。工具会更新,内核行为会变化,但这套思路——先全局后局部、先指标后进程、先趋势后尖峰——在哪个阶段都不过时。