1. 从"虚拟内存统计"说起:为什么需要一张系统运行快照
前几天处理一个客户的服务器问题,现象非常典型:白天业务正常,一到晚上跑批任务就卡顿,top上看CPU和内存都正常,但就是慢。现场同事折腾了半天没头绪,我远程上去先敲了一条命令:
vmstat 2 5输出里wa列的数字让我立刻锁定了方向——CPU大量时间在等待I/O,结合跑批任务的写入特征,判断是磁盘队列拥塞。后来确认是某些批处理脚本产生了严重的随机写放大。最终的修复其实不复杂,但如果没有vmstat这张"系统运行快照",排查还得绕好几个小时。
这就是我写这篇文章的动机。很多运维同行习惯把top当作第一排查工具,但在定位"系统整体是否健康"这类问题上,vmstat提供的是完全不同的视角——它把CPU、内存、I/O、中断、上下文切换、交换分区等关键指标压缩到一张表里,几秒钟就能判断病根在哪个子系统。
这篇文章不是抄man page,而是从实际使用角度把vmstat讲透:每条输出代表什么、哪些指标组合能定位问题、最优参数怎么敲、常见误读怎么避开,以及它和类似工具(free、top、iostat、sar等)如何配合。无论你是刚接手服务器的运维新手,还是想加深系统调优理解的后端开发,读完应该能把这工具用得比绝大多数同事更专业。
2. 先弄明白vmstat的设计逻辑:一屏数字背后的内核数据源
vmstat全称是Virtual Memory Statistics,名字里带"virtual memory"是因为它的历史渊源——早期版本是专门统计虚拟内存子系统行为的。但随着Linux内核的发展,它逐渐演变成一台"系统健康状态监视器",虚拟内存相关指标只是其中一部分。
它的工作原理非常简单:vmstat通过读取/proc/vmstat、/proc/meminfo、/proc/stat这几个内核维护的计数器文件,把原始计数换算成可读的指标,再格式化成表格打印。你甚至可以不用vmstat命令,直接看这几份文件也能得到同样信息,但人肉换算太痛苦,工具存在的意义就是把这些枯燥的计数器变成一眼能看懂的数值。
2.1 三条命令区分使用场景
# 一次性快照,适合快速看一眼系统是否健康 vmstat # 每隔2秒采样一次,共采集5次,适合观察波动和短期趋势 vmstat 2 5 # 持续采样,直到手动Ctrl+C终止,适合长时间观察 vmstat 2注意第一和第二条的区别:第一条输出的是"自系统启动以来到当前时刻"的平均值,这个数据意义不大——毕竟它把昨天乃至上个月的负载都平均进去了;第二条才是真正有用的实时采样。
vmstat 2 5是我日常使用频率最高的一条命令,它构建了两层信息:第一条(表头下方第一行)依然是从开机到现在的平均值,而后续每行则是上一个采样周期内的增量值。
2.2 procs、memory、swap、io、system、cpu六大区域的完整解读
vmstat输出分六个区域,很多人记住表头就完事了,但表头缩写背后对应的内核计数器你最好也搞清楚,这样遇到异常指标时才能快速判断是内核哪个子系统出了状况。
| 区域 | 字段 | 含义 | 背后数据源 |
|---|---|---|---|
| procs | r | 运行队列中的进程数(含正在运行和等待CPU的) | /proc/stat中的procs_running |
| procs | b | 不可中断睡眠进程数(典型是等I/O) | /proc/stat中的procs_blocked |
| memory | swpd | 已使用的交换空间总量(KB) | /proc/meminfo |
| memory | free | 空闲物理内存(KB) | /proc/meminfo |
| memory | buff | 用作块设备缓存的物理内存(KB) | /proc/meminfo |
| memory | cache | 用作页缓存的文件缓存(KB) | /proc/meminfo |
| swap | si | 从磁盘交换入内存的速率(KB/s) | /proc/vmstat中的pswpin |
| swap | so | 从内存交换出到磁盘的速率(KB/s) | /proc/vmstat中的pswpout |
| io | bi | 从块设备接收的块数(每秒) | /proc/vmstat中的pgpgin |
| io | bo | 发送到块设备的块数(每秒) | /proc/vmstat中的pgpgout |
| system | in | 每秒中断数(时钟中断+硬件中断) | /proc/stat中的intr增量 |
| system | cs | 每秒上下文切换次数 | /proc/stat中的ctxt增量 |
| cpu | us | 用户态CPU时间占比 | /proc/stat中的cpu逐项计算 |
| cpu | sy | 内核态CPU时间占比 | /proc/stat中的cpu逐项计算 |
| cpu | id | 空闲时间占比 | /proc/stat中的cpu逐项计算 |
| cpu | wa | 等待I/O完成的时间占比 | /proc/stat中的cpu逐项计算 |
| cpu | st | 被虚拟机管理程序偷走的时间占比 | /proc/stat中的cpu逐项计算 |
这里我重点说几个容易被忽略的字段。
procs下的b字段:很多人只看r(运行队列),却不看b(阻塞在I/O上的进程数)。当b持续大于0,尤其是耦合着cpu的wa偏高,说明系统存在明显的I/O瓶颈。有一次排查数据库性能问题,vmstat 1采样里b列长期在5以上,wa在30%左右,系统响应缓慢。那时top里看到的进程CPU占用并不高,但大量线程在等磁盘。如果不看b和wa,单纯优化SQL可能完全不解决问题。
swap下的si和so:这两个字段一出现非零值,基本可以判定物理内存吃紧。特别是so(换出)持续大于0时,说明内存压力已经很严重,内核在不停地把匿名页写回交换分区。我见过不少服务器内存加到64G甚至128G,但应用层存在内存泄漏,导致si/so依然频繁跳动。
system下的cs:上下文切换次数是判断系统"忙得有没有效率"的关键指标。如果cs数值很高(比如每秒几万甚至数十万),但cpu的us和sy都不高,说明系统可能出现了锁竞争或者过度调度,大量CPU时间消耗在线程切换上。多线程应用优化时,这个字段是衡量"线程数量是否合理"的一种参考。
cpu下的st:只有虚拟机或容器环境才会有明显数值,st高意味着宿主机CPU资源被抢占。假如你在云主机上发现st长期超过10%,应用再慢也别急着调优自己的代码,先考虑是不是宿主机超卖太严重。
2.3 一次输出实例的完整解读示范
我在一台测试机上跑vmstat 2 3,得到如下输出:
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 0 0 6248620 86544 3114216 0 0 12 23 7 12 4 4 92 0 0 5 0 0 6243284 86544 3114268 0 0 912 5406 1102 2334 31 14 43 12 0 3 0 0 6239988 86544 3114300 0 0 724 4021 989 2102 28 13 50 9 0第一行是系统启动以来的平均值,参考意义有限。从第二行开始才是真实采样:r从2涨到5,cs从12跳到2334,说明系统并发负载明显上升;io的bi和bo突然出现数百到数千的量级,cpu的wa从0跳到12%,明显在等I/O。此时如果鼠标再敲一条iostat -x 1,大概率能抓到具体的磁盘设备利用率到瓶颈。这就是vmstat真正的用法——它告诉你"哪里可能有问题",再由更精细的工具去确认。
3. 六类指标组合定位:按场景拆解而不是孤立看数字
vmstat的每个字段单独看价值有限,真正值钱的是字段之间的组合关系。这就像看体检报告,单一指标超标的信号和几个指标一起超标的信号,诊断方向完全不同。我把常见场景拆成几类,按这套思路去套,排查效率会高很多。
3.1 CPU瓶颈:r和us/sy的组合解读
当procs下的r持续大于CPU核数的2倍以上,但us和sy都接近饱和(合计接近100%),基本可以认定是CPU计算资源不足。注意r不是"正在运行的进程数",而是"正在运行+等待运行的进程数",所以r远大于核数意味着大量进程在排队。
举个例子,一台4核机器,vmstat采样中r稳定在8左右,us在70%以上。虽然sy不高,但CPU已经明显超载。此时优化方向有两个:先看看应用层有没有死循环或者无效计算,如果没有,那就得扩容。这里不要被id=0欺骗——id为0说明CPU一点空闲都没有,但这不等于"所有核都在干活",只是"所有核都在排队干活"。
3.2 内存压力:free、si、so三者联动
很多人看到free剩下几百MB就开始紧张,其实没必要。Linux对物理内存的使用策略是"尽量多用",free数值低恰恰说明缓存策略生效,磁盘数据尽量缓存在内存里。真正需要紧张的是si和so连续出现非零值,尤其是so。
还是看组合:free持续下降且si/so持续走高,说明进程的匿名页开始被换到磁盘。此时top看单个进程的RES也许并不突出,但系统的整体性能已经在下滑。我处理过一起案例,Java应用因堆外内存增长过快,内存逐渐被耗光,swap活动频繁。当时vmstat 1里so已经到每秒几百KB,但free还剩下几个G(因为cache会先释放),如果不看si/so很难发现内存已经告急,后来加了一行内存监控告警才彻底解决。
3.3 I/O瓶颈:wa和bi/bo的组合
wa持续偏高是典型I/O瓶颈信号。但wa高也可能是CPU在等待锁或磁盘控制器响应慢,所以要看io区域的bi/bo是否真有大量数据传输。如果wa高但bi/bo都很低,说明CPU阻塞在等待上而不是真正有I/O在跑,这时要考虑是不是设备控制器卡了或者存在I/O路径上的锁竞争。
有次现场环境让我印象很深:某系统vmstat 1显示wa高达40%,但bi/bo都不过百。我下意识以为是磁盘慢,后来排查发现是存储适配器驱动有bug,导致I/O请求经常超时。换驱动后wa立刻从40%掉到2%以下。这就是为什么不能只看wa,必须把io字段组合着看。
3.4 上下文切换风暴:cs高但CPU空闲
这个场景最迷惑人。你看到cs每秒几万次,系统却像死了一样,但us/sy都不高,id还有余量。出现这种组合,通常是线程数量过多导致的调度风暴,或者是有大量锁竞争。
怎么定位?先用pidstat -w 1看哪个进程贡献了最多的上下文切换,再用perf sched统计调度事件。如果确认是锁竞争,最直接的缓解办法是减少锁粒度,或者用无锁数据结构替代;如果是线程池线程数配置过高,调低线程数量往往立竿见影。这个场景我建议你特别留意cs的绝对值——超过CPU核数乘以5000就可疑,乘以10000基本就是风暴了。
3.5 虚拟化环境:st字段的警钟
云主机的vmstat里如果st长期不为0,说明宿主机CPU超卖严重,你的虚拟机在"偷时间"。这种情况下应用慢不一定是你自身代码的问题,不要盲目扩容纵向资源,而是要考虑换一台宿主负载更低的主机,或者干脆加副本做横向伸缩。
4. 实际排查案例:一次数据库变慢的完整定位链
理论讲完了,用一个真实排查案例把整个思路串起来。有一回线上MySQL实例响应明显变慢,慢查询日志却空空如也,无法通过SQL维度定位。
我第一反应就是先看系统层面有没有变化。连接服务器后执行:
vmstat 1 10输出里r从平常的1-2涨到了6-8,wa(I/O等待)在25%-30%之间波动,si/so都为零。r偏高有一定CPU压力,但wa也高说明I/O也在拖后腿。这种组合意味着"CPU想干活,但在等磁盘",所以我重点排查磁盘。
接着用iostat -x 1看具体盘符,发现sda的%util接近100%而await到了200ms以上,确实磁盘接近饱和。再往下看是哪些进程在产生大量I/O,用iotop一抓,发现是个夜间批处理任务在同时跑多条全表扫描的UPDATE语句。
最后的处理手段并不复杂:一是把批处理任务的并发度调低,二是把部分查询从主库切到只读从库。改动后再次用vmstat 1 5验证,r回落到2,wa掉到5%以内。整个排查链路大概半小时,其中vmstat花了不到10秒就给出了方向。
这个案例最能说明vmstat的定位:它不直接告诉你答案,但它能大幅缩小问题范围,让你知道该往CPU、内存还是I/O方向去查,后续的精细化定位工具才能派上用场。
5. 高频参数和进阶用法:不要只会敲vmstat
vmstat默认输出已经很有用,但很多参数能让它适配更多场景。挑几个高频用法分享一下。
5.1 采样次数和间隔的组合规则
# 每个2秒采样1次,共采集3次 vmstat 2 3 # 持续采样,不限制次数 vmstat 2第一行永远是系统启动以来的平均值,想跳过这一行可以用-a,它会显示活跃和非活跃内存的信息,输出布局稍有不同。不过实际使用中我一般不去掉第一行,原因很简单:第一行可以作为"这台机器历史负载"的参考,特别是接手别人环境时,第一行的r和wa大致能反映这台机器长期的健康程度。
5.2 -s参数:查看内存事件汇总
vmstat -s这个参数会把大量内存相关的统计事件打印出来,包括总内存、已用内存、活跃页、非活跃页、换入换出总量、页错误数等。因为全部是累计值,适合用来和当前采样做对比,比如想知道开机以来总共发生了多少次主缺页中断,这个参数是最直接的。
注意:缺页中断分为主缺页(需要磁盘I/O读入)和次缺页(直接从内存页表分配),前者对性能影响大,后者基本可以忽略。
vmstat -s输出的major fault就是主缺页,如果你发现它持续快速增长,说明应用的内存访问模式可能存在问题,或者内存配置偏小。
5.3 -d和-p参数:磁盘维度的补充
# 各磁盘的读写统计 vmstat -d # 指定分区的读写统计 vmstat -p /dev/sda1这两个参数可以在不额外安装工具的情况下,快速查看磁盘粒度的I/O统计。虽然精细程度不如iostat,但胜在系统自带,在没有额外软件包的环境里应急排查很管用。
5.4 -a参数:活跃与非活跃内存
vmstat -a输出会增加act和inact两列,分别展示活跃和非活跃内存的大小。Linux回收内存时会优先回收非活跃页,因此inact偏高说明有大量"冷数据"躺在内存里,如果要给应用程序腾内存空间,这些非活跃页是可以被回收的。不过现实中我更常用cat /proc/meminfo里的Active和Inactive,内存维度更细。
6. 和其他工具配合:vmstat、top、free、iostat、sar如何分工
不少运维新手有个误区:以为top能代替vmstat。确实top上面几行也显示负载、CPU、内存,但top的核心价值是"进程视角",它把单个进程的CPU和内存消耗列得清清楚楚;vmstat则是"系统视角",它整体告诉你哪个子系统有问题。两者定位完全不同,严格来说是互补关系。
| 工具 | 核心视角 | 最擅长的场景 | 不足 |
|---|---|---|---|
| vmstat | 系统整体 | 快速定位CPU、内存、I/O瓶颈方向 | 无法定位到具体进程 |
| top | 进程个体 | 看哪个进程吃CPU/内存最多 | 系统整体健康度弱化 |
| free | 内存详细 | 看内存总量、可用量、缓存构成 | 只看内存,不涉及CPU和I/O |
| iostat | 磁盘设备 | 看块设备的利用率、队列长度、服务时间 | 不包含内存和CPU |
| sar | 历史趋势 | 回溯历史性能数据,定位"昨天发生了什么" | 需要提前部署并开启采集 |
我的习惯打法是这样的:
- 接到问题反馈,先敲
vmstat 1 5,判断方向是CPU、内存还是I/O。 - 方向是CPU,用
top -Hp看具体是哪个线程在消耗CPU。 - 方向是内存,用
free -h和cat /proc/meminfo看内存构成;如果si/so异常,再用top按RES排序找进程。 - 方向是I/O,用
iostat -x 1看具体磁盘设备,用iotop找I/O大户。 - 需要回溯,用
sar查看历史负载趋势,判断问题是突发的还是渐进的。
这套打法虽然简单,但效率极高。绝大多数性能问题在vmstat阶段就能确认方向,后面几步只是进一步坐实,避免白忙一场。
7. vmstat常见误读:这些数字陷阱你踩过几个
vmstat虽然命令简单,但输出里藏了不少坑。我把这些年见到的误读场景集中说一下。
7.1 free小就等于内存不足
新手看到free显示几百MB就恐慌,实际上Linux的cache会按需释放给应用程序。看内存是否充足,正确指标是free命令里available一列,它才是"在不触发swap的情况下还能分配多少内存"。vmstat的free列只是"完全空闲的物理页",忽略cache的存在。
7.2 wa高就一定是磁盘故障
wa高只能说明CPU在等I/O,但不一定是磁盘坏了。前面说过,可能是驱动bug、存储链路拥堵、锁等待。先看bi/bo确认是否真有I/O流量,再看iostat的await和%util,如果流量不大但等待时间长,问题大概率在设备或驱动,不在磁盘本身。
7.3 r高就等于CPU不够
r高说明有大量进程在排队,但排队也可能是锁竞争导致的调度停滞。之前遇到一个Java应用疯狂创建线程的场景,r高达30但CPU的us只有20%,系统响应极慢。后来排查发现是线程池参数配置错误,导致任务不断创建新线程,加剧了上下文切换,形成了调度风暴。这种情况下加CPU核数毫无意义,反而可能让问题更严重。
7.4 第一行的数值被误当作实时状态
这是最隐蔽的坑。vmstat不带参数时输出的是"系统启动以来的平均值",你用它来判断当下状态完全可能得出错误结论。我见过有人看着第一行的r=0.2,就说"系统很空闲",但实际上此刻服务器已经被压垮了——因为开机时间很长,平均值被稀释了。养成好习惯:vmstat永远跟采样间隔和次数一起用。
7.5 st高被误以为是CPU耗尽
st是虚拟机被宿主机抢占的CPU时间,云服务器里如果st高,意味着宿主机资源超卖或邻居虚拟机太嘈杂。这时你不要急着优化自己系统的代码,而是考虑调整部署位置或换宿主机,优化自己代码解决不了宿主机抢占问题。
8. 几个可以立刻用起来的小技巧和扩展
最后分享几个我在实际使用中总结的小技巧,谈不上高深,但确实能省时间。
技巧一:结合watch命令动态刷新
watch -n 2 'vmstat 1 3'watch会把vmstat的输出定时刷新在同一屏,适合长时间观察变化趋势。注意外层watch -n 2控制每2秒刷新一次,内层vmstat 1 3则是每次刷新前采集3次每隔1秒的数据,这样实际看到的是约3秒窗口内的平均状态,比单纯每秒刷一次更平滑。
技巧二:脚本里用vmstat做简单告警
如果不想引入复杂的监控平台,写个简单的shell脚本就能实现基础告警:采样vmstat 1 3,判断最后一行输出的wa如果连续多次超过80,就通过邮件或消息接口报警。内核自带的监控方案虽然简陋,但很多轻量场景够用。
技巧三:把vmstat和性能压测配合使用
做压测时,终端开着一路vmstat 1,压测脚本跑完后再看日志,能清晰记录每一秒的系统状态。这个做法比压测结束后再看汇总数据有价值得多——你能看到瓶颈是何时出现的、哪项指标先报警,这对调优方向极有指导意义。
技巧四:处理大量输出时用日志记录
如果系统性能是间歇性出现问题,人肉盯屏幕不现实。可以这样记录一段时间内的采样,比如每5秒一次,记录10分钟:
vmstat 5 120 > /tmp/vmstat_$(date +%Y%m%d_%H%M%S).log等下次问题复现时,这份日志能帮你重建"故障前后系统状态的变化过程"。我排查一些偶发性卡顿时,经常靠这类历史日志找规律。
9. 从vmstat看Linux整体调优思想
工具讲得差不多了,我想从方法论层面做个总结。很多人学了一堆命令却还是不会排查问题,本质原因是没有理解Linux性能调优的核心思想。
vmstat的六个区域(procs、memory、swap、io、system、cpu)不是孤立设计的,它们构成了一个循环模型:运行队列长度(procs.r)反映CPU供需关系,内存和交换(memory、swap)反映资源的下层供给,I/O(io)反映磁盘链路,system反映中断和调度,cpu则汇总时间片去向。任何性能问题,在这个循环里一定能找到断裂点。
回到最开始的数据库变慢案例:r升高说明任务在排队,wa升高说明排队等的是I/O,bi/bo升高说明I/O确实繁忙,于是问题锁定在磁盘链路。整个推理链路如果走对了,基本一次到位。
所以我的建议是:不要孤立地记命令参数,而是建立"系统是一条流水线"的心智模型。CPU是工人,内存是工位旁的临时堆放区,磁盘是原料仓库,I/O是传送带,上下文切换是工人换岗。哪一环堵了,哪一环的指标就会异常。vmstat不过是把每一环的状态都搬到你面前而已。
模型建立之后,vmstat在你的工具箱里就不再是一条生硬的命令,而是一张系统健康仪表盘。配合top、iostat、sar,你完全可以在不借助商业监控软件的情况下,独立完成绝大多数性能问题的定位。这套方法论,是我这些年排查各类诡异性能问题最值钱的积累。