服务器突然变卡,top 里 CPU 没满、内存也够,可 load average 一路飙高,网站慢到怀疑人生——这种八成是磁盘 IO 打满了。它和「磁盘空间满」不是一回事:空间满是没地方写,IO 满是写得太多或太慢,磁盘忙不过来。下面用 iostat、iotop 两步把「谁在狂写、哪块盘在喘」揪出来。
先确认是不是 IO 瓶颈
别一上来就瞎猜,先用两条命令看证据:
top # 看 %wa(iowait)那一行,持续高于 10% 就指向磁盘
iostat -x 1 # 看设备级 %util 和 await
iostat -x 1 重点看两列:%util 接近 100% 说明这块盘在满负荷跑;await 是平均每次 IO 等待毫秒数,SATA SSD 正常就几毫秒,飙到几十上百就是严重排队。如果只是偶尔尖刺,多半是备份或日志刷盘,不用慌;持续高位才是真瓶颈。
用 iotop 抓「捣乱」的进程
知道是 IO 问题后,下一步锁定进程:
iotop -oP # 只看真正在搞 IO 的进程,按 IO 排序
-o 只显示有 IO 的进程,-P 按进程(不是线程)聚合。常见凶手就那几个:MySQL 在刷脏数据、某个服务日志疯狂写、半夜的备份/rsync、或者索引重建。找到 PID 你就知道该调哪里了。
| 命令 | 看什么 | 异常信号 |
|---|---|---|
| iostat -x 1 | 设备 %util / await | %util≈100%、await 几十 ms |
| iotop -oP | 进程级 IO 占用 | 某进程长期占 80%+ IO |
| df -h | 盘挂在哪个目录 | 定位到具体挂载点 |
用 iostat 定位是哪块盘
iostat -x 1 按设备分行,先找出 %util 高的盘(比如 sdb),再 df -h 看这块盘挂到哪个目录。这样你才能判断是数据库盘、日志盘还是网站目录在遭罪。多盘机器尤其要分清,别一通优化搞错盘。
踩坑实录
有台 8G 内存的机器,白天网站偶尔卡几秒,top 看 CPU 才 30%、内存也够,一直以为是网络问题。后来 iostat -x 1 一看,sda 的 await 长期 180ms、%util 100%,iotop 里 mysqld 占了 90% IO。根因是 innodb_buffer_pool_size 只给了 512M,数据量一大,查询全变成磁盘随机读。把 buffer pool 调到 4G、顺手把机械盘换成 SSD 后,await 直接掉到 2ms,卡顿消失。教训:iowait 高别光盯 CPU,先问磁盘。
常见根因与对策
| 根因 | 表现 | 对策 |
|---|---|---|
| MySQL 刷脏页 | mysqld IO 高 | 调大 innodb_buffer_pool / innodb_io_capacity |
| 日志无轮转 | 某日志文件狂写 | 配 logrotate + truncate 应急 |
| 备份/rsync 抢资源 | 夜间定时任务期卡 | ionice/nice 限速,错峰执行 |
| 机械盘随机写 | await 长期偏高 | 换 SSD / 加缓存层 |
临时止血与长期治理
临时止血:ionice -c 3 -p <pid> 把失控进程 IO 优先级降到最低,必要时直接 kill。这属于救火,根因还在。
长期治理:上 SSD、按上面的表调数据库参数、给备份任务限速并错峰、再加监控。推荐 node_exporter + Grafana 看磁盘 IO 利用率历史曲线,设个 80% 告警,别等卡了才查。顺带一提,如果连带出现「磁盘空间满」,可以看这篇 Linux 磁盘空间满了怎么办 一起对照排查。
常见问题
iowait 高但 iotop 里没看见进程?
可能是文件系统元数据操作(如大量小文件删除),或进程刚结束但脏页还在刷。直接看 iostat -x 1 的设备列更准。
云服务器 IO 是不是被限速了?
有可能。云盘有 IOPS/吞吐上限,突发额度用尽就限流。去云监控看磁盘 IO 是否触顶,必要时升配或换高性能云盘。
await 多少算异常?
SATA SSD 一般 <10ms,机械盘 <20ms;持续几十 ms 就是明显拥挤,该优化了。
怎么提前预防?
监控磁盘 IO 利用率并设置告警,比等网站卡了再排查省心得多。
如果你也遇到过磁盘 IO 飙红的离谱场景,欢迎到 fenij.com 评论区聊聊当时是怎么定位的。