服务器负载飙到十几,CPU却大量空闲:时间都花在等磁盘

来源:互联网 时间:2026-10-28

uptime报load average: 15.32, 14.87, 13.02,三个数都挂在十几,机器是四核。top第一行反过来说92.3% idle,wa那一栏38.4%。CPU基本闲着,负载是核数的四倍,两个数摆在同一个屏幕上,方向其实已经指出来了:排队的进程不在等CPU,是在等磁盘。四核机器上load到4就是满载,15意味着有三倍多的活儿在排队等着。

load average统计的是R和D两种状态的进程。R在跑或等CPU,D是不可中断睡眠,多在等IO。CPU闲着负载却高,多出来的全是D,这类进程top默认不显示。

ps -eo stat,pid,ppid,cmd | grep '^D' 把D状态的进程列出来,数量通常不多,几个就够把负载顶上去,因为它们在队列里一直挂着不退出。看到mysqld,往下查慢查询;看到php-fpm,多半在读写session或者本地缓存文件;看到rsync、tar,那就是备份任务在跑。想再细一层,用ps查那个进程的内核栈,会看到wait_on_page_bit、io_schedule这类名字,指向的都是磁盘。

iostat -x 1看两个指标。%util接近100说明设备一直没停;await是单次IO的平均耗时,机械盘超过20毫秒、SSD超过5毫秒就该警觉了。两个都高是队列堆起来了,%util不高而await很高,是单次IO本身慢,云盘和网络存储上很常见。iostat第一次输出是开机以来的平均值,不要看,等第二次以后。

最常见的来源是MySQL缺索引。一条没走索引的查询在几十万行的表上扫一遍,随机读把磁盘队列塞满,PHP那边全在等数据库返回,D状态就一堆一堆地冒出来。第二个来源是小文件太多,session存本地、缓存存本地,一次请求几十次open和read,单看每次都不大,加起来就是队列。第三个是备份,凌晨跑tar或者rsync,读整个站点目录,跟白天的业务抢同一块盘。这三类里第一类最容易改,见效也最快。扫一遍慢日志,前十名通常能覆盖七成的IO。

还有一个容易漏的:swap。free -h看一眼,swap used不是零,说明内存不够,页被换出去了,读回来就是一次磁盘IO。这种负载是内存问题伪装成IO问题。swapon -s看一眼,swap是分区还是文件,文件搁在机械盘上,换出换进慢得更明显。

处理方向不是加CPU核数。你加核数上去,D状态的进程还在那儿等,load average的分母变了,数字反而更高。要么把随机读变成顺序读,要么把读挪走:加索引、把session挪到Redis、把备份放到从库或者业务低峰时段。把读挪走这件事,往往比优化SQL来得快。

改完再跑一次iostat,对比await。数字降下来才算改对了。降不下来,去看那块盘的队列深度和云厂商给的IOPS上限,有些盘买的时候就把上限写死了,程序怎么调都过不去。顺手把监控阈值改成按核数算百分比,别写死一个数字。

相关文章

标签:

A5创业网 版权所有