服务器为什么"越跑越慢",而 CPU 占用看着却不高
很多站长都遇到过一个诡异现象:服务器刚上线时一切正常,跑了一段时间后,某些操作开始变慢——数据库查询偶尔卡顿、编译或备份时间变长、SSH 敲命令有明显的停顿感。登上去一看,top 里 CPU 占用不高,内存也还有余量,磁盘 df 更是离写满还远。于是无从下手。
这种情况里,有一个非常常见却被忽视的原因:CPU 因为过热而降低了运行频率。当散热跟不上、机箱风道被堵、或者机房温度偏高时,CPU 会自动降频来保护自己。降频是保护机制,不是故障,但它会让整机性能"无声地打折"——CPU 占用率看起来一样,实际每秒干的活却少了一截。本文讲的就是如何用 Linux 自带的工具,把这类"看不见的硬件层性能损失"查出来。
第一层:看 CPU 当前实际跑在什么频率
先别急着装任何工具。/proc/cpuinfo 里就有当前频率:
grep -m1 "model name" /proc/cpuinfo grep "cpu MHz" /proc/cpuinfo
每条 cpu MHz 对应一个逻辑核心的当前频率。如果你看到所有核心都稳定在标称频率的一半甚至更低,而且此时系统并不空闲,那就值得警惕。但这个数字是瞬时值,会跳动,最好多看几次、或者在压测时看。
更规范的做法是用 cpupower(来自 linux-cpupower 包):
apt install -y linux-cpupower cpupower frequency-info
输出里的关键几行是:driver(驱动,比如 intel_pstate 或 acpi-cpufreq)、current policy(当前的 governor、频率上下限)、current CPU frequency。你要特别关注"频率上界"这一项——如果它被设成了远低于 CPU 标称最大频率的值,那性能就被硬性限制住了。常见于云服务器厂商为了控制功耗而设的 scaling_max_freq,也常见于你自己或某个调优脚本改过之后忘了改回来。
第二层:读懂 governor 决定"什么时候升频"
CPU 频率不是恒定的,它由 governor(调频器) 决定在什么负载下升到多高。几种常见取值:
performance:直接锁在最高频。"性能最好、最费电、最容易热"。powersave:尽量待在低频,按需缓慢升频。省电但可能"跟不上"突发负载。ondemand/schedutil:按负载动态调整,是最常见的默认值。conservative:比 ondemand 更保守地升频,升得慢。
查看与切换:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
如果默认是 powersave 或某个偏保守的策略,而你的服务器是"常年有稳定负载"的类型(比如跑着网站、数据库),那么临时切成 performance 往往能明显改善响应速度:
cpupower frequency-set -g performance
但这个改动重启就丢。要持久化,不能只改 /sys(那是内核暴露的运行时接口,写进去只是临时的)。正确做法有两条路:
# 路线一:安装 cpufrequtils 并用其配置文件 apt install -y cpufrequtils echo 'GOVERNOR="performance"' > /etc/default/cpufrequtils systemctl restart cpufrequtils # 路线二:systemd 服务在开机时下发 # 写一个 unit,ExecStart=cpupower frequency-set -g performance
这里要强调一个判断原则:不要无条件把 governor 设成 performance。云服务器上,很多实例的 CPU 是共享的,你锁死最高频并不一定能拿到更多物理 CPU,反而可能因为功耗和散热策略触发更严重的降频。正确做法是先测——在改之前和改之后各跑一次同样的负载,用 cpupower frequency-info 的实时频率 + 实际任务耗时来对比,用数据决定,而不是凭感觉。
第三层:用 lm-sensors 读出温度与风扇
频率问题的根子往往是温度。要读温度,最通用的是 lm-sensors:
apt install -y lm-sensors sensors-detect # 一路回车,探测本机的传感器芯片 sensors
sensors-detect 会把探测到的内核模块名写进 /etc/modules-load.d/ 或 /etc/modules,这样重启后还能加载。sensors 的输出会列出若干组温度,常见的有:
Package id 0或Core X:CPU 各核心温度,最常见的判据。触到接近high/crit阈值就说明散热有问题。fan1、fan2:风扇转速(RPM)。如果某路fan显示为 0 或N/A,可能是风扇没转、线没接、或该通道没接传感器——对物理服务器值得排查。in0等电压:一般不用管,除非你怀疑供电问题。
虚拟机的坑:大多数云 VPS 里 sensors 读不到真实温度,因为虚拟化层不向客户机暴露物理传感器。你会看到 sensors-detect 说没找到任何芯片,或者 sensors 输出为空。这不是工具坏了,是环境限制。云上的"硬件层监控"要靠厂商提供的控制台或 API(有的提供 CPU 积分/超限信息),而不是 lm-sensors。
看内核有没有"主动降频"的痕迹
除了温度,内核还可能因为其他原因限制频率。这些信息都在 dmesg / journald 的内核日志里:
dmesg | grep -i -E "thermal|throttl|temperature|microcode" journalctl -k --since "1 hour ago" | grep -i -E "thermal|throttl"
几个关键词的含义要分清:
CPU0: Core temperature above threshold, cpu clock throttled:字面意思是"温度超阈值,CPU 时钟被节流"——这是最直接的过热降频证据。thermal相关的其他提示:说明触发了温度管理策略。mce(Machine Check Exception)或microcode更新日志:硬件层的纠错事件,如果频发要留意 CPU 或内存是否真的有硬件问题。
如果你看到 clock throttled 这类日志,基本可以确定性能损失来自硬件层。接下来的动作是:检查机箱/机房散热、清灰、确认风扇正常、必要时申请换机房或加散热。这类问题的根治在物理层面,软件再怎么调都只是缓解。
区分"降频"和"被限制":两件不同的事
排查时一定要把两个概念分开,否则会走错方向:
- 降频(throttling):CPU 本来能跑高频,但因为过热/功耗被动态压下来。特征是频率忽高忽低、伴随温度日志。解决方向:散热、环境温度。
- 频率限制(capping):
scaling_max_freq被设成了一个较低的固定值,CPU"永远到不了高频"。特征是频率稳定地卡在一个上限、也没有过热日志。解决方向:改回或调整scaling_max_freq、检查是谁设的。
判断方法:如果 cpupower frequency-info 里 hardware limits 显示的最大频率远高于 current policy 的 max,那就是被软件限住了(属于第二种);如果两个上限一致、但当前频率经常掉下来且伴随高温,那就是降频(第一种)。把这一条搞清楚,能替你省下大量瞎调参数的时间。
虚拟化环境下的额外变量
在 KVM/云主机里,还有几个特有的坑:
- CPU 型号被伪装:宿主机可能把
model name伪造成一个通用型号("Intel Xeon"之类的泛化名),/proc/cpuinfo看到的频率也不一定真实。别把这里的数字当成物理真相。 - CPU steal time:这是虚拟化最该看的指标,表示"你的 vCPU 想跑但被宿主机调度走了"的时间占比。用
top看%st,或者mpstat -P ALL 1看%steal。steal 持续偏高(比如 >5%)说明你的机器在同宿主机上被邻居抢了资源,这比降频更能解释"CPU 不高但就是慢"。这时候唯一有效的动作是联系厂商换机器/升配,调 governor 没用。 - CPU 积分/突发(burstable 实例):一些便宜的 VPS 采用"积分制"——平时攒积分,需要时消耗积分来突发。积分用完就被压回基线性能。这种"忽快忽慢"也常被误当成降频。看厂商控制台的积分面板,或
cpupower frequency-info之外的厂商 API 才能确认。
把监控做成常态,而不是出事了才查
上面讲的都是"事后排查"。真正省心的做法,是把关键指标做成定期采集、留下历史、能看趋势,而不是等性能出问题才临时登机器看一眼。对个人站长来说,不需要上 Prometheus 那一套重家伙,用最容易落地的办法就够了:
- 定时采样温度与频率:一条 cron,每小时把
sensors和cpupower frequency-info的关键行追加到一个日志文件,配上日期。跑一周后你就有了一条温度曲线,能看出"是不是下午机房升温就掉频"这种规律。 - 记录 steal time 趋势:把
mpstat -P ALL 1 1的%steal列也采下来。如果它随时间缓慢上升,说明宿主机在变拥挤,是换机器或升配的明确信号。 - 给阈值配告警:温度超过某个值、频率持续低于某个值、steal 超过 5%,都值得触发一次通知(邮件、TG bot 都行)。告警的意义是让你在问题还小的时候就介入。
这套东西不复杂,但它把"靠感觉"变成了"看数据"。运维里最贵的成本从来不是工具,而是"我不知道机器正在变坏"这种信息盲区。哪怕只是三个每小时的采样点,坚持积累就足以让判断有据可依。
一份可直接照做的排查清单
grep "cpu MHz" /proc/cpuinfo看当前频率,空闲时是否异常偏低。cpupower frequency-info对比hardware limits与current policy的 max,判断是"降频"还是"被限频"。cat scaling_governor看调频策略,决定是否切换(先测后改)。sensors看温度与风扇(物理机);云机跳过此项。dmesg | grep -i "throttl\|thermal"找过热降频的直接证据。top里的%st或mpstat的%steal,排查虚拟化资源被抢。- 改完
governor/max_freq后一定要持久化(cpufrequtils 配置或 systemd unit),并重跑一次负载对比数据。
最后提醒一句:硬件层性能问题,软件只能"诊断"和"缓解",不能"治愈"。如果确认是散热或宿主机争抢导致的,该做的是换环境——把时间花在真正能解决的层面,比在 /sys 里反复折腾参数划算得多。