ChatGPT、Codex排查实录:CPU明明只有20%,为什么Linux Load Average却突然飙到几十?
线上接口突然开始变慢。不是所有请求都失败但 P95、P99 明显往上走。第一反应通常是CPU是不是打满了打开top一看却很奇怪CPU使用率只有20%左右。甚至还有大量idle。Java进程也没有明显Full GC机器内存还有剩余。可再看load average: 38.2, 34.7, 29.1Load Average已经飙到了几十。这时候最容易产生一个疑问CPU都没跑满为什么Load还能这么高甚至有人会直接怀疑“Linux的Load指标是不是不准”其实不是。真正的问题在于Load Average和CPU利用率根本不是同一个指标。CPU只有20%并不能证明机器没有大量任务正在“排队”。一、先给核心判断Load高不等于CPU一定高很多人第一次理解Load Average时会简单认为Load就是CPU使用率。这其实不准确。CPU利用率主要描述的是CPU时间到底有多少比例正在执行任务。而Load Average更接近当前有多少任务正在运行或者正在等待系统资源。在Linux里除了真正等待CPU执行的Runnable任务以外一部分处于不可中断睡眠状态的任务也会被计算进Load。所以完全可能出现CPU利用率只有20%。但Load Average已经达到30、50甚至更高。这说明问题不一定发生在CPU算力而可能发生在IO、磁盘、NFS、块设备或者其他不可中断等待上。二、一个很典型的线上现场比如一台8核机器。平时load average: 2.1, 1.8, 1.5突然某个时间点变成load average: 31.5, 26.8, 19.3再看CPUus 12% sy 6% id 78% wa 4%CPU甚至还有接近80%的Idle。但接口已经明显变慢。这时候如果继续围绕JVM参数。线程池大小。CPU扩容排查很容易走偏。因为真正可能发生的是大量线程进入D状态。也就是Uninterruptible Sleep。三、D状态为什么会把Load顶起来Linux进程状态里经常会看到RSD这些状态。其中R通常表示Running或者Runnable。也就是正在CPU执行或者准备等待CPU调度。而D通常表示任务正在等待某些不可中断的内核操作完成。最常见就是磁盘IO。块设备。NFS。某些文件系统操作。驱动或存储相关等待。这些任务虽然没有真正消耗大量CPU但仍然可能被计入Load。所以出现CPU低 Load高时我第一反应不会是CPU不够。而是有没有大量D状态进程或线程。四、最快先看到底有多少R和D遇到这种问题可以先用ps -eo state,pid,ppid,comm,wchan:32 | egrep ^[RD]重点不是记命令而是看当前有没有大量R或者D状态任务。如果主要是大量R更像CPU调度压力。如果大量是D方向就完全不同。应该优先往磁盘。文件系统。网络存储。块设备。内核等待继续查。这一步可以很快把问题分成两条路。五、第一种情况CPU不高但大量线程卡在磁盘IO假设一个Java服务正在大量读取文件。或者数据库在刷盘。或者日志量突然暴涨。业务线程频繁访问磁盘。如果底层存储突然变慢线程会不断等待IO完成。这些线程不需要持续占用CPU所以CPU利用率看起来并不高。但它们大量堆积以后Load就会上升。这种现场通常还会伴随接口延迟上涨。请求队列增长。磁盘响应时间升高。应用线程数量上升。有时甚至CPU越低接口反而越慢。因为线程根本没机会真正运行。都在等IO。六、这时候不要只看IO Util%很多人会打开iostat看到磁盘%util没到100%就说磁盘没问题。这也很容易误判。因为磁盘问题不能只看一个%util还应该结合await队列长度。读写延迟。IOPS。吞吐。具体块设备。例如一块盘%util只有60%但await已经从2ms升到80ms。对于线上接口来说已经足够把延迟放大很多倍。所以排磁盘问题时我更关注IO到底等了多久。而不是只看磁盘有没有“跑满”。七、第二种情况NFS或者远程挂载卡住这类问题特别容易制造CPU很空Load非常高。比如应用读取NFS挂载目录。远程文件系统。云盘。某个网络存储。平时都正常。某一刻网络抖动或者存储端响应变慢。大量进程进入D状态。CPU几乎没什么压力。可Load一路上涨。这时候你看Java CPU。GC。数据库。可能全都正常。但业务线程其实都堵在文件系统调用。如果使用wchan或者线程栈继续看可能会发现大量线程停在IO Wait。文件系统。块设备相关位置。这时候根因根本不在Java代码本身。八、第三种情况磁盘并不忙但底层设备出现卡顿还有一种情况更难发现。监控看起来平均IO不高。吞吐也不大。但某些时刻设备延迟突然尖刺。比如磁盘错误重试。云盘抖动。RAID异常。块设备延迟。文件系统问题。这时候平均指标不一定很夸张。但某一小段时间内大量线程可能同时卡住。Load就会迅速上升。所以排查时一定要把Load升高时间点和磁盘延迟。内核日志。应用P99。文件系统事件对齐。平均值正常不代表尖峰没有问题。九、第四种情况大量Runnable任务但CPU看起来还没满还有一种情况和D状态不同。系统里确实有大量Runnable任务。但你看到总体CPU只有20%。这时候要继续看是不是某个CPU核心已经被打满。或者任务被CPU亲和性限制。例如一台32核机器某个关键进程只能跑在2个核。那总体CPU利用率可能只有6%到10%。但那两个核心已经100%。大量线程在这两个核心上排队。于是系统总体CPU看起来不高。某个局部调度域却已经严重拥堵。所以还要继续看per-CPU usage。CPU affinity。cpuset。容器CPU限制。不能只看全机平均CPU。十、容器环境里还要小心CPU LimitKubernetes里尤其容易出现这种错觉。宿主机可能64核。整体CPU利用率只有20%。但某个Pod只分到了2核CPU Limit。Pod内部线程很多。不断争抢这2核。宿主机层面看起来一点都不忙。但Pod内部已经出现明显排队。这时候如果只看Node CPU完全正常。真正需要看的是Pod CPU Limit。CPU Throttling。cgroup指标。线程Runnable数量。所以“机器CPU不高”这个结论在容器环境里还必须继续问这个服务自己真正能用多少CPU十一、Load Average到底多高才算异常很多人喜欢记一句Load不能超过CPU核数。这个说法能作为粗略参考但不能机械套用。比如8核机器Load 8不一定立刻有问题。如果任务都很短、系统延迟稳定可能还能正常工作。真正应该结合CPU核数。业务延迟。Runnable数量。D状态数量。IO等待。队列长度一起判断。更重要的是看趋势。如果平时Load一直在2左右突然变成20哪怕机器是32核也值得查。因为变化本身就说明系统运行状态发生了明显改变。十二、最快排查顺序我建议按这7步走以后遇到CPU不高但Load Average突然飙升可以直接按这个顺序。第一步确认Load升高时间点看1分钟。5分钟。15分钟Load。判断是突然尖峰还是持续升高。第二步看CPU分布不要只看总体CPU。看每个核心。看user。system。idle。iowait。第三步统计R和D状态判断是CPU排队为主还是不可中断IO等待为主。第四步如果D状态多立即查IO看磁盘延迟。队列。NFS。块设备。文件系统。第五步如果R状态多查CPU调度看单核打满。CPU affinity。容器CPU Limit。Throttling。第六步对齐应用线程栈看业务线程究竟卡在文件读写。Socket。锁。JNI。系统调用。还是纯CPU计算。第七步把系统指标和接口P99对齐最终要回答到底哪个资源等待直接对应了业务变慢。十三、为什么只看top特别容易被误导top非常方便。但它往往只是一个当前瞬间快照。如果问题是周期性IO抖动。短时间大量D状态。CPU局部热点。容器Throttling。单纯看一眼top很容易错过。所以这种问题更适合结合vmstatpidstatiostatps以及应用监控一起看。重点不是工具多。而是让不同Evidence回答不同问题。十四、vmstat里我最先看什么这种场景里vmstat很有用。我通常先看r和b。r可以帮助判断有多少Runnable任务在等待CPU。b则可以帮助观察有多少任务处于不可中断等待。如果你看到CPU还有大量idle但b持续很高那就非常值得往IO和设备等待方向继续排。反过来r特别高而某些CPU核心持续100%就更像调度和CPU资源局部不足。十五、一个很典型的案例日志盘卡住Java接口跟着慢假设服务平时日志量不大。某次发布以后突然增加大量DEBUG日志。应用每个请求都写多条同步日志。开始时CPU仍然只有20%。但磁盘延迟逐渐升高。写日志线程阻塞。部分业务线程也等待相关IO。接着D状态增加。Load从3涨到25。接口P99从200ms涨到3秒。这时候如果只看Java CPU不高。Heap正常。GC正常。你甚至可能开始怀疑下游服务。其实真正根因只是日志IO把本机存储拖慢了。所以排查Load异常一定要问最近是不是有日志量变化。文件操作变化。备份任务。大批量写盘。数据库刷盘。NFS操作。这些看起来和CPU没关系的事情。十六、如果是NFS卡顿怎么验证假设你怀疑某个挂载目录。最重要的不是“感觉像NFS”。而是找Evidence。例如Load升高时大量D状态进程的wchan都指向文件系统等待。应用线程栈大量卡在文件读取。NFS相关指标同时异常。远程存储响应时间上升。把这些时间点对齐。如果挂载恢复以后。D状态下降。Load下降。接口P99恢复。那因果链就比较完整。十七、如果是CPU局部打满怎么处理比如全机32核。某服务因为Affinity只绑定到2个核。这两个核持续100%。大量线程Runnable。全机CPU平均看起来只有6%左右。这时候真正该修的不是磁盘。而是CPU绑定。容器CPU Request / Limit。线程数。任务并发度。热点线程。所以“CPU低 Load高”不能直接等于IO问题。要先通过R和D把方向分开。十八、为什么盲目扩CPU可能完全没用如果真正根因是NFS卡住。磁盘延迟。块设备等待。你从8核扩到16核可能一点改善都没有。因为任务根本不是缺CPU。它们是在等设备返回。甚至CPU更多以后可能允许更多并发任务同时进入IO路径反而把底层存储压得更严重。所以扩容前一定要先确认当前Load到底是CPU Queue还是IO Wait Queue。十九、修完以后怎么验证这类问题不能只看Load降了。还应该同时看D状态线程有没有恢复。Runnable队列有没有下降。磁盘await是否恢复。IO队列长度是否恢复。CPU Throttling是否下降。接口P95 / P99是否恢复。比如修复前Load 35。D状态80个。磁盘await 70ms。P99 4秒。修复后Load 3。D状态接近0。await回到3ms。P99回到300ms。这才说明真正的阻塞点已经解除。二十、ChatGPT、Codex在这种排查里怎么用更有效不要只问CPU不高为什么Load这么高最好把Evidence一起给出来uptimetopvmstatiostatR / D状态数量。线程栈。容器CPU Limit。异常时间点。接口P99曲线。这样ChatGPT、Codex才能帮你判断更像CPU调度。IO等待。NFS卡顿。磁盘延迟。还是cgroup限制。AI最适合做的是把多个系统指标串成同一条因果链。而不是看到一个Load数字以后直接猜。二十一、Plus和Pro怎么判断如果你只是偶尔让ChatGPT、Codex分析一次top。看几段vmstat、iostat。判断R/D状态。帮你整理一条排查链。这种任务比较集中Plus通常已经够用。如果你的日常已经是持续排查Linux。Kubernetes。Java。磁盘。NFS。容器Throttling。线程Dump。系统调用。并且一个性能问题要连续分析大量指标、日志、代码和压测结果这种高频、多轮、长时间工程排障已经成为常态那Pro会更适合。关键不是Load高不高级。而是ChatGPT、Codex是不是已经持续参与你的完整性能排障流程。最后CPU明明只有20%为什么Linux Load Average却能突然飙到几十因为CPU利用率低不代表系统里没有大量任务正在等待。Load高可能来自Runnable任务排队。大量D状态。磁盘IO延迟。NFS卡顿。块设备异常。CPU Affinity。容器CPU Limit。所以遇到CPU低 Load高不要先扩CPU。更不要只看一眼top。真正应该先走的排查链是Load时间点 → CPU分布 → R/D状态 → IO / 调度 → 线程栈 → 容器限制 → P99验证一旦先分清到底是在等CPU还是在等IO后面的排查范围通常会迅速缩小。很多看起来“Linux指标互相矛盾”的问题最终不是指标错了而是我们一开始理解错了Load到底在统计什么。持续分享 Codex、大模型开发与 AI 编程实战内容也整理了稳定的 Plus/Pro 订阅渠道有需要下方可自取。