
如果你的 Linux 服务器 CPU 占用不高、内存还剩一大半但业务就是卡得不行网上所有“加机器”的建议都没解决你的问题——那我很确定十有八九是磁盘 I/O 在拖后腿。这时候 Linux 自带的两个经典工具 iotop 和 iostat 就是你的第一手侦察兵iostat 告诉你整块磁盘忙不忙、延迟有多高iotop 告诉你到底是哪个进程在背后疯狂读写。两个工具配合着用基本就能把“全盘性能问题”和“具体进程行为”串成一条完整的排查链路。这篇笔记没有玄学只有我在日常运维和性能排查中反复用过的命令、指标判读经验和踩坑记录。适合被“莫名其妙的慢”折磨过的后端开发、运维同学也适合刚接触系统性能分析、想搞明白磁盘指标到底怎么看的初学者。1. 先问一句你的瓶颈真的在磁盘吗iostat 和 iotop 是两种视角1.1 磁盘 I/O 出问题时的典型“伪装”磁盘 I/O 问题有个很讨厌的特点它很少直接报警而是伪装成“应用很慢”。最常见的表现是应用接口响应变慢数据库连接池堆积队列里全是等待中的请求。你打开 top 一看CPU 使用率只有百分之二三十内存也还有不少剩余但系统的 load average 已经飘到十几甚至几十。这里有个新手非常容易忽略的细节load average 高并不一定代表 CPU 忙它统计的是处于可运行状态和不可中断睡眠状态的进程总数。当进程在等待磁盘 I/O 完成时它进入的是不可中断睡眠状态同样会计入 load average。所以你会发现 CPU 明明是闲的系统却“感觉累得要死”。更直接的信号是在 top 的进程状态列里看到大量字母 D。D 状态是 uninterruptible sleep也就是进程正在等内核里的某个 I/O 操作返回。如果这种进程一大片基本可以断定问题出在 I/O 路径上而不是计算资源不够。此时再去看磁盘指标方向就对了。1.2 一个看路面一个看车辆说到磁盘 I/O 监控iostat 和 iotop 很容易被放在一起比较甚至有新人会问“这两个是不是重复了”。它们完全不是一回事也不是替代关系。用一条公路来类比iostat 相当于路面监控告诉你整条路现在的拥堵指数是多少、车辆平均通过时间是多长、单位时间能跑多少辆车。它关心的是“这条路本身的状态”。iotop 则像交警站在路口的视野它能看到具体是哪一辆车、哪个司机在占着车道甚至能看出这辆车是正在走走停停还是已经堵到不动了。它关心的是“到底谁在占用 I/O”。从实现层面看iostat 来自 sysstat 工具包读取的是内核暴露的块设备统计信息数据源头是 /proc/diskstatsiotop 则是基于内核的 taskstats 机制直接统计每个进程和线程的 I/O 行为。所以 iostat 给的是设备级别视角iotop 给的是进程级别视角。正常流程应该是先用 iostat 确认盘面确实有问题再用 iotop 定位到具体肇事进程。1.3 什么时候优先用哪个我习惯的选型逻辑很简单场景首选工具原因系统整体变慢不确定谁在读写先 iostat先用设备级指标确认方向怀疑某块盘或某个分区异常iostat -x看吞吐、延迟、队列已经锁定“肯定有进程在狂写”iotop -o直接找具体进程需要确认磁盘队列是否拥堵iostat await/avgqu-sz设备级排队时间比进程占比更直观需要留下进程级历史记录iotop -b批处理模式输出到文件2. iostat 实用手册读盘面水位先分清 %util 和 await2.1 安装与第一组命令iostat 不是单独的包它属于 sysstat 工具集。Debian/Ubuntu 上用apt install -y sysstatCentOS/RHEL 上用yum install -y sysstat。安装完直接执行iostat就能看到开机以来的统计摘要。我在排查问题时几乎不用默认输出因为默认只给 tps 和读写速率信息量不够。最常用的排查命令是iostat -x -d 1 5这里的-x表示显示扩展指标-d表示只看设备不看分区可以配合-p ALL查看所有分区1是采样间隔秒数5是采样次数。再加上-k或-m可以把读写数据量从块数换算成 KB 或 MB更方便人类阅读iostat -x -d -k 1 5输出大致是这个样子不同内核版本和 sysstat 版本列名会略有差异Device r/s w/s rkB/s wkB/s await avgqu-sz %util sda 12.3 98.5 1204.4 40980.2 45.60 1.34 96.8这里有一个新手最容易踩的坑第一次输出的那一行是系统开机以来的平均累计值从第二行开始才是这个采样周期内的真实值。所以采样次数最好至少给 2 次否则你看的是历史不是当下。2.2 重点字段到底怎么判读扩展输出里的字段很多真正判读时我通常关心这么几个。r/s和w/s是每秒读、写请求次数。rkB/s和wkB/s是每秒实际读写的数据量。这两组数据主要回答“设备在做什么、量级有多大”。如果写入量特别大但 w/s 不高说明单次请求很大比如全量拷贝如果 w/s 很高但数据量不大说明是小碎块频繁写比如日志文件被不断 append。await是请求从进入 I/O 队列到最终完成所花时间的平均值单位毫秒。这个指标直接反映“一次读写请求要等多久”。对机械盘来说几十毫秒算正常范畴超过 100 毫秒就属于偏慢对 SSD 和 NVMe 来说正常应该在几毫秒到十几毫秒的量级如果看到几十毫秒说明盘面或者队列已经有问题了。avgqu-sz是设备 I/O 队列的平均长度。排队越长说明积压越严重。这个值和 await 往往是联动的。%util是采样周期内设备有 I/O 请求在处理的时间占比。注意我用了“有请求在处理”这种说法因为很多人把它理解成磁盘容量使用了多少这是不对的。%util达到 100% 只代表在整个采样周期里设备没有一秒空闲并不等于“盘已经到物理极限了”。尤其对于并行能力很强的 SSD 和 NVMe即使%util100%它可能依然能继续处理新的并发请求反过来机械盘的随机读写场景里%util到 90% 以上通常确实意味着接近性能天花板。还有一个老生常谈但值得提醒的字段svctm。老版本 iostat 里有个svctm平均服务时间很多教程还在引用但 sysstat 新版已经把这个字段移除了因为它计算逻辑有问题在生产环境不具参考价值。你看到旧文章里教你看 svctm 大小时可以自动忽略。2.3 三种典型“表情”直接套用iostat 数据拿到手后不要孤立地看某个字段我通常按下面三组模式快速判断第一组%util高await也高。这是最典型的“盘真的忙不过来了”。常见于机械盘上的随机读写或者长期高负载的数据库服务器。这时候再配合查看写盘进程基本就是定位到某个持续消耗 I/O 的任务。第二组%util高await不算高。这种情况说明设备确实一直在处理请求但每个请求完成得并不慢。多见于并发能力强的固态盘。它的问题可能不是延迟而是整盘带宽被大量请求吃掉了需要看吞吐量是否已经逼近设备上限。第三组%util不高await却很高。这个信号最有意思——盘面看起来不太忙但每个请求都要等很久。我遇到这种模式时第一反应是排队不是发生在磁盘自身而是发生在更上层的链路上。比如多块盘组成的 RAID 卡、虚拟化环境里宿主机对磁盘的统一调度、或者 cgroup 配置了 IO 限流都会出现“盘前不忙、盘后排队”的现象。把这三种模式对应到实际场景里排查方向就不会跑偏。3. iotop 实用手册把 I/O 账单精确到线程3.1 权限和安装少了它你什么都看不到iotop 的安装也很简单Debian/Ubuntu 是apt install -y iotopCentOS/RHEL 是yum install -y iotop。但很多人装完发现输出全是一堆 0或者直接报 Permission denied原因是没有用 root 权限。iotop 需要读取内核里的 task I/O 统计信息非 root 用户没有这个权限。所以运行的时候要么直接sudo iotop要么切到 root 用户。绝大多数发行版的内核都默认开启了它依赖的统计功能普通服务器环境不用额外编译内核参数。如果你是在容器里跑要格外注意容器默认权限往往受限即使容器内是 root也可能没有权限读取宿主机的任务统计。这时候 iotop 看到的数据可能是不完整的。不要在容器里纠结这个问题直接到宿主机上排查。3.2 参数不多这几个组合就够用iotop 是交互式工具默认打开之后会全屏动态刷新。但在排查和脚本化场景里我常用的其实就那么几个参数组合。sudo iotop -o -k -d 2-o表示 only show processes or threads actually doing I/O也就是只显示真正在发生读写的进程而不是把满屏的零读写进程都列出来。-k是显示成 KB/s看的时候不费眼睛。-d 2是每两秒刷新一次。这个组合适合现场观察屏幕上剩下的基本就是“犯罪嫌疑人清单”。sudo iotop -b -n 3 -d 1 -o -k-b是 batch mode也叫批处理模式。在这个模式下 iotop 不会进入全屏交互而是按间隔打印几次后退出。-n 3表示打印 3 次-d 1表示间隔 1 秒。这个命令特别适合写进脚本里抓现场或者重定向到文件里做记录。sudo iotop -P -o -k-P是只看进程不看线程。默认情况下 iotop 会把一个进程的多个线程拆开单独显示这虽然定位细致但有时候线程太多反而难看清。遇到 JVM、数据库这类多线程应用我更推荐先加-P从进程维度看确定是哪个进程之后再取消-P去具体线程里挖。3.3 交互按键和输出字段懂这两个就够用交互模式下有几个按键是真正高频的左右方向键切换排序字段p切换进程/线程显示a切换累计 I/O 和即时 I/Oo开关“仅显示有 I/O 的进程”q退出。记住这几个其他人机交互按键基本用不上。输出来看重点关注这几列DISK READ和DISK WRITE采样间隔内实际读、写磁盘的速率。注意这是“落到磁盘上的量”不是进程请求了多少量缓存命中部分不会体现。SWAPIN进程发生 swap 换入时等待的时间占比。如果这列很高说明内存在紧张系统在频繁换页问题根源可能不是写盘。IO进程等待磁盘 I/O 完成的时间占比。这列越高说明进程越是被 I/O 卡住。COMMAND命令名。配合前面的进程号就能锁死目标。有一点要提醒IO高不等于磁盘一定整体饱和。某个进程 90% 的时间在等 I/O也有可能是因为它在等一个特定设备的慢请求而整块盘其实还闲着。所以我在现场从来不只看 iotop而是先让 iostat 在旁边同时跑着两个数据互相印证判断才靠谱。4. 真实排查链路从 iostat 发现异常到 iotop 揪出元凶再到留痕与告警4.1 一条完整的四步排查路径我平时遇到“系统慢但 CPU 不高”的典型排查链路是这样的可以把它当成固定套路来用。第一步用top确认进程状态。如果看到大量 D 状态进程同时 load average 偏高基本确认是 I/O 问题进入下一步。第二步用 iostat 看盘面iostat -x -d -k 1 5假设输出中/dev/sda的%util长期在 95% 以上await从正常水平的 10 毫秒涨到了 80 毫秒那就可以确定磁盘这块是整个链路的瓶颈。第三步用 iotop 找进程sudo iotop -o -k -d 2这时候屏幕上会有一个或几个进程的DISK WRITE非常高IO列也接近 90%。把进程号记下来再用 ps 看它对应的完整命令行确认是什么任务。第四步判断它该不该出现在这里。如果是一个定时备份任务正在全量扫描数据目录或者日志服务正在大量回写文件那 I/O 高就是“有理由的”如果是个不相关的程序在异常写盘就得赶紧处理。这里我不建议一看到高占用就 kill先搞清楚任务性质否则可能把正常业务进程直接打死。4.2 常见根因和处置方向对照根据我自己的排查经验进程级高 I/O 最常见的几类原因和处理思路可以整理成一张表遇到类似场景直接对号入座。常见根因确认方法处理方向定时全量备份或数据扫描crontab 时间点与高 I/O 时间段吻合错峰执行或使用ionice -c 3降低 I/O 优先级日志文件轮转不及时单文件过大日志目录占用高写日志进程 I/O 高配置按大小轮转开启压缩限制单文件大小数据库全表扫描或缺少索引慢查询日志增加iostat 中 r/s 升高优化 SQL、补充索引必要时限流内存不足导致频繁换页SWAPIN列高free -h内存告急增加内存或调整 swappiness 参数虚拟化环境物理盘被邻居影响整机 iostat 显示 I/O 高单进程不明显检查宿主机负载考虑更换实例或磁盘4.3 监控留痕和最小告警脚本排查完现场之后还有一个常常被忽略的工作把监控数据留下来。没有历史数据下次再出事你还是靠猜。我习惯用一个非常简单的脚本做定时采集#!/bin/bash LOG_DIR/var/log/io-monitor mkdir -p $LOG_DIR TS$(date %Y%m%d_%H%M%S) iostat -x -d -k 1 10 $LOG_DIR/iostat_${TS}.txt sudo iotop -b -n 3 -d 1 -o -k $LOG_DIR/iotop_${TS}.txt # 只保留最近 30 天 find $LOG_DIR -name *.txt -mtime 30 -delete配合 crontab 每小时跑一次每天会生成 24 组快照。等到下次出现卡顿时直接翻对应时间段的文件省去“事后回忆”的麻烦。唯一要注意的是iotop -b输出会比 iostat 大不少采集频率别太高否则监控日志本身也在消耗磁盘。如果还想再进一步可以写一个用 awk 做的简易告警比如每分钟看一次 iostat发现%util超过 80 就往告警文件里写一行iostat -x -d -k 1 3 | awk /^sd/{if ($NF 80) print strftime(%F %T), $0} /var/log/io-alert.log这个脚本按设备名匹配$NF取最后一列也就是%util。它不精细但作为零依赖的兜底告警足够用了。生产环境最好还是接入已有的监控系统但原理都一样把系统级指标和进程级现场一起留存才能做事后复盘。最后分享一点个人体会工具用熟了以后你会发现iostat 和 iotop 真正的价值不在于“能跑出多少指标”而在于帮你建立对系统的判断力。我最开始排查 I/O 问题时也犯过只看单个指标的错——看到%util100% 就断言盘挂了结果换了一块新盘问题依旧。后来才明白任何指标都要结合设备类型、业务模式、时间特征一起看机械盘和 SSD 的判据完全不同白天高峰和凌晨批处理任务的表现也完全不同。所以我现在的习惯是平时用 iostat 积累设备健康基线了解自己的系统在空闲和正常负载下是什么水位出问题时先 iostat 看盘面表情再 iotop 定位进程最后回到业务层判断“该不该减负”。这套流程虽然朴素但在绝大多数“慢得莫名其妙”的场景里都能兜住底。希望这篇笔记也能给你提供一条同样清晰的路径。