前一天,我刚刚针对 WordPress 文章详情页的 CDN 缓存进行了一轮优化,重点解决带 Query String 的 URL 无法有效复用缓存的问题。
原本我以为,处理完这一类缓存穿透以后,服务器 CPU 告警应该会有所缓解。
但 2026 年 7 月 28 日下午,阿里云 ECS 再次出现 CPU 使用率接近 100% 的告警。
这一次,我没有继续围绕文章详情页和 Query String 打转,而是从阿里云监控开始,一路检查:
- Nginx Access Log
- PHP-FPM 日志与 Slowlog
- W3 Total Cache Object Cache
- Redis Slowlog
- W3TC Disk Enhanced Page Cache
- 实际 Cold Cache 与 Cache HIT 的响应时间
最终发现:
这次 CPU 满载并不是上一篇文章中“详情页 + Query String”问题的简单延续。更核心的问题是:服务器只有 1 vCPU,而大量不同 URL 在 Page Cache 未命中时,需要进入 WordPress 动态生成页面。只要同时出现几个高成本 Cold MISS,就足以让 PHP-FPM Worker 全部繁忙,把唯一的 CPU 核打满。
其中一个很有代表性的页面是:
/page/9/
第一次 Cold MISS 时,TTFB 达到:
10.602379 秒
W3TC Page Cache 建立以后,第二次访问只需要:
0.016091 秒
这个差距最终成为本轮优化方向的关键依据。
一、阿里云 CPU 再次持续接近 100%
这次告警发生在 2026 年 7 月 28 日下午。
查看阿里云 ECS 操作系统监控,可以看到 CPU 从大约 14:33 开始迅速升至接近 100%,一直维持到 14:42 左右才突然下降。
与此同时,内存使用率整体仍然保持稳定,没有出现与 CPU 同步耗尽的情况。
因此,首先可以判断:
这一次并不是典型的内存不足,而是 CPU 计算资源出现了持续饱和。

进一步打开阿里云进程监控以后,可以看到高 CPU 进程基本都是:
php-fpm
php-fpm: pool www
说明这次负载的主要来源并不是 Nginx,而是 WordPress PHP 动态请求。

二、Nginx 出现大量 499
生产站当前 Nginx Access Log 位于:
/data/wwwlogs/www.shuijingwanwq.com_nginx.log
提取 14:32~14:44 这一时间窗口以后,共得到:
844 个请求
状态码分布如下:
499 499
200 153
301 130
404 62
也就是说:
499 占到了约 59%。
Nginx 的 499 表示客户端在服务器完成响应之前主动关闭了连接。
单独看到 499,并不能证明 PHP-FPM 就是根因。
但是按分钟统计后,情况变得非常明显:
14:32 total=53 499=0
14:33 total=73 499=49
14:34 total=153 499=147
14:35 total=38 499=30
14:36 total=46 499=39
14:37 total=59 499=47
14:38 total=61 499=42
14:39 total=56 499=31
14:40 total=78 499=58
14:41 total=89 499=49
14:42 total=65 499=7
14:43 total=42 499=0
14:44 total=31 499=0
这和阿里云 CPU 曲线几乎同步:
14:33
CPU 接近 100%
499 开始大量出现
14:34~14:41
CPU 长时间高位
大量 499
14:42
压力开始快速下降
14:43
499 恢复为 0
因此,这批 499 更像是:
PHP-FPM 已经严重拥塞以后,客户端、CDN 或爬虫等待过久而提前断开连接的结果。
三、这不是单个 IP 或单个 URL 导致的
最开始,我怀疑是不是某一个页面被大量请求。
但统计 URL 后发现,请求非常分散。
一级路径分布大致如下:
/tag/ 373
/category/ 102
/en/ 85
/2026/ 83
/page/ 76
/ 25
/wp-json/ 24
...
其中:
/tag/
/category/
/page/
三类页面加起来,占这一时间窗口请求量的约 65%。
具体 URL 也非常分散,例如:
/tag/app/
/tag/mysql-5-7/page/3
/tag/electron/
/page/9
/page/19?query-62-page=61
/page/109?query-62-page=127
/category/.../page/17/?noamp=mobile
...
来源 IP 同样比较分散,没有看到某一个 IP 独占大部分请求。
User-Agent 中则出现了不少自动抓取程序,例如:
Sogou web spider
bingbot
SemrushBot
Baiduspider
meta-externalagent
ClaudeBot
Googlebot
因此,这次更像是:
多种爬虫或自动化程序同时遍历大量历史文章、Tag、Category、分页等不同 URL,而不是一个 IP 对一个地址进行简单洪水请求。
四、这一次不是 Query String 主导的问题
前一天,我刚刚处理过:
文章详情页 + Query String
导致 CDN 和 W3TC 缓存不能有效复用的问题。
所以最开始我自然怀疑是不是同一个问题又出现了。
但统计这次 499 后发现:
带 Query String:135
不带 Query String:364
也就是说,大约:
73% 的 499 请求根本没有 Query String。
因此,前一天的优化仍然有意义,但它并不能解决这一次 CPU 告警。
可以把两类问题区分开:
上一篇重点解决的是:
同一个文章详情页
+
不同无意义 Query String
+
产生不必要的缓存分裂或穿透
这一次面对的则更像:
大量不同 URL
+
各自第一次 Cold Cache
+
同时进入 PHP 动态生成
五、PHP-FPM 的 7 个 Worker 被全部用完
PHP-FPM 当前已经开启 Slowlog:
request_slowlog_timeout = 3s
slowlog = /usr/local/php/var/log/slow.log
request_terminate_timeout = 600s
也就是说,单个 PHP 请求执行超过 3 秒,就会记录 Slowlog。
查看这次事故窗口的 PHP-FPM 日志以后,出现了非常关键的一条:
[28-Jul-2026 14:32:57]
WARNING: [pool www] server reached max_children setting (7), consider raising it
也就是说:
在整机 CPU 正式进入长时间 100% 平台之前,PHP-FPM 的 7 个 Worker 已经全部繁忙。
而且从 14:32 开始,多个 Worker 就不断出现:
executing too slow
整个 14:32~14:44 时间窗口中,共记录:
287
次 executing too slow。
PHP-FPM 日志明确记录了 max_children=7 被占满,而且此前已经存在多个超过 3 秒的动态请求。
以其中 PID 49712 为例,阿里云进程监控中可以看到,它在整机 CPU 告警期间长时间保持较高 CPU 使用率,并在大约 14:42 以后快速下降。

至此,CPU 告警的运行过程已经基本清晰:
动态 PHP 请求变慢
↓
PHP-FPM Worker 逐渐被占用
↓
7 个 Worker 全部繁忙
↓
新请求开始等待
↓
CPU 长时间接近 100%
↓
客户端等待过久
↓
Nginx 出现大量 499
六、Slowlog 最初把嫌疑指向 Gutenberg、W3TC 和 Polylang
进一步分析 PHP-FPM Slowlog 后,完整调用栈中出现频率较高的文件包括:
1237 wp-includes/class-wp-block.php
760 Gutenberg navigation.php
190 W3TC ObjectCache_WpObjectCache_Regular.php
189 W3TC ObjectCache_WpObjectCache.php
188 W3TC Cache_Redis.php
185 Polylang translatable-object.php
按插件统计:
1149 gutenberg
706 w3-total-cache
313 polylang
16 tms-extensions-polylang
6 post-views-counter
6 organize-series
...
这最开始很容易让人认为:
Gutenberg Navigation
↓
taxonomy / term
↓
Polylang
↓
W3TC Object Cache
↓
Redis
就是主要问题。
但完整调用栈只代表:
这些组件参与了慢请求。
并不能说明哪一个步骤真正占用了最多时间。
于是我进一步统计:
PHP-FPM 在执行超过 3 秒、被抓取 Slowlog 快照的那个瞬间,PHP 正在执行什么函数?
七、约 73% Slowlog 快照停在 Redis GET/MGET
结果非常集中:
135 get() W3TC Cache_Redis.php
53 mget() W3TC Cache_Redis.php
总计:
188 / 257 ≈ 73%
相比之下:
18 Gutenberg Theme JSON sanitize()
7 merge()
3 mysqli_query()
2 Navigation get_inner_blocks()
...
于是调查重点一度转到了:
W3TC Object Cache → Redis
查看 W3TC 的 Cache_Redis.php 后,可以看到实际读取逻辑中存在:
$v = $accessor->get( $storage_key );
因此 Slowlog 中大量出现 get(),确实对应底层 Redis Object Cache 读取。
八、Redis 也真的出现了慢请求
Redis 当前整体状态并没有明显的容量问题。
内存:
used_memory 517.82 MB
used_memory_peak 556.27 MB
maxmemory 1.00 GB
而且:
evicted_keys 0
blocked_clients 0
rejected_connections 0
Object Cache 累计:
keyspace_hits 149242618
keyspace_misses 1894018
命中率约为:
98.75%。
因此,没有看到 Redis 因为内存耗尽而大量淘汰缓存的迹象。
但是 Redis Slowlog 中确实存在十几到几十毫秒的请求。
例如:
GET optionsalloptions
约 87.6 ms
另外,有一条:
MGET terms...
后面直接显示:
... (675 more arguments)
也就是说,一次 MGET 中读取了大约数百个 Term Cache Key。
一度看起来:
Redis 很可能就是服务器变慢的根因。
直到继续查看 ECS CPU 核心数。
九、真正改变判断的是:这台服务器只有 1 vCPU
执行:
nproc
结果只有:
1
也就是说:
整台 ECS 只有一个 CPU 核心。
同时 Redis 也运行在本机:
redis-server 127.0.0.1:6379
这一下就把之前很多看似分散的问题串起来了。
服务器实际上是在让:
PHP-FPM Worker × 7
Redis
Nginx
系统其他进程
一起争抢唯一的 CPU 核。
因此,当 7 个 PHP-FPM Worker 同时执行比较重的 WordPress 动态请求时,很容易形成:
PHP 大量计算
↓
唯一 CPU 核饱和
↓
Redis 也需要争抢 CPU
↓
Object Cache GET/MGET 延迟增加
↓
PHP 请求需要更长时间才能完成
↓
Worker 更久无法释放
↓
系统持续处于高 CPU
所以:
Redis 的确出现了慢请求,但它很可能既是热点,也是 CPU 饱和以后被拖慢的受害者,而不是最初的起火点。
这也意味着,此时不能简单按照 PHP-FPM 提示:
consider raising it
就把:
pm.max_children = 7
提高到 14 或更大。
1 vCPU 并不会因为 PHP Worker 增加而得到更多计算资源。
十、真正关键的问题:W3TC Page Cache HIT 后到底快不快?
既然问题可能来自大量请求进入 PHP,那么接下来最重要的事情,就是检查 W3 Total Cache Disk Enhanced 到底有没有生效。
当前配置:
Page Cache:Enabled
Engine:Disk Enhanced
Cache Query String:false
我直接绕过 CDN,通过源站测试几类 URL。
Tag 页面
/tag/app/
三次 TTFB:
0.023816 秒
0.087154 秒
0.016832 秒
Category 页面
/category/program-development/smslib/yuntongxun/
结果:
0.160287 秒
0.079870 秒
0.052548 秒
文章详情页
/2026/03/27/9383/
结果:
0.045637 秒
0.016188 秒
0.015945 秒
这些数据说明:
当 W3TC Disk Enhanced 已经有可用缓存时,源站本身可以非常快。
真正异常的是下面这个分页页面。
十一、一个页面从 10.6 秒变成 0.016 秒
测试:
/page/9/
第一次:
HTTP=200
TTFB=10.602379s
第二次:
TTFB=0.016091s
第三次:
TTFB=0.031990s
与此同时,第一次访问以后,新生成了:
wp-content/cache/page_enhanced/www.shuijingwanwq.com/page/9/_index_slash_ssl.html
时间正是:
2026-07-28 16:14:01
因此,这一次几乎完整复现了:
第一次
Page Cache MISS
↓
进入 PHP-FPM
↓
WordPress 完整动态生成
↓
10.6 秒
↓
写入 Disk Enhanced
第二次
直接使用 Page Cache
↓
0.016 秒
所以本轮排查最重要的发现,并不是:
W3TC Page Cache 不工作。
恰恰相反:
W3TC HIT 后非常快,但 Cold MISS 的代价非常高。
十二、为什么每分钟一百多个请求也能打满服务器?
这次 14:34 请求最多的一分钟大约只有:
153 个请求
单纯看数字,似乎并不算特别高。
但是放在当前环境中:
1 vCPU
+
WordPress 块主题
+
大量插件
+
部分 Cold MISS 页面需要数秒甚至 10 秒
以后,性质就完全不同了。
如果爬虫不断访问:
/tag/A/
/tag/B/
/tag/C/
/category/A/
/category/B/
/page/9/
/page/19/
/page/109/
...
每一个 URL 都可能是独立的 Cold MISS。
即使每个页面生成以后都可以缓存:
URL A:第一次 MISS
URL B:第一次 MISS
URL C:第一次 MISS
URL D:第一次 MISS
只需要同时有几个高成本动态请求,就足以把 7 个 PHP-FPM Worker 占满。
十三、W3TC Page Cache 已经接近 1 GB
当时:
wp-content/cache/page_enhanced
已经达到:
918 MB
总文件数量:
7320
缓存文件年龄分布:
1 小时内: 1414
1~2 小时: 1564
2~6 小时: 4342
6 小时以上: 0
W3TC 当时的配置则是:
Maximum lifetime:3600 秒
Garbage Collection Interval:3600 秒
Cache Preload:关闭
看到这里,我最开始怀疑:
Page Cache 是否每过 1 小时就失效,然后不断重新生成?
但是实际测试表明,情况没有这么简单。
十四、超过 3 小时的缓存仍然可以直接 HIT
我从现有缓存中找到:
/2023/11/21/12193/
缓存生成时间:
2026-07-28 12:48:52
而测试已经到了大约 16:21。
此时请求:
TTFB=0.038573s
请求前后:
mtime 不变
size 不变
inode 不变
页面尾部还明确显示:
Served from: www.shuijingwanwq.com @ 2026-07-28 12:48:52 by W3 Total Cache
因此可以确认:
磁盘上超过 3 小时的缓存仍然可能被直接返回,并不是到了 3600 秒以后立即从磁盘删除或者每次请求必然重新生成。
所以,我也放弃了:
“3600 秒以后全部重新 Cold MISS”
这种过于简单的理解。
十五、没有启用 Cache Preload
W3TC 当前:
pgcache.prime.enabled = false
也就是没有开启 Page Cache 预热。
理论上,Cache Preload 可以提前生成页面,从而减少真实访客承担 Cold MISS 的概率。
但这一次,我暂时没有启用。
原因很简单:
服务器只有 1 vCPU
而且已经实际遇到:
/page/9/
Cold MISS = 10.6 秒
对于拥有大量文章、Tag、Category 和分页的站点,如果主动批量生成大量页面缓存,本身就可能形成持续 CPU 压力。
所以现阶段,我更倾向于:
让已经花费大量 CPU 生成的缓存尽量保留得久一点,而不是主动制造一轮新的大规模预热。
十六、最终只修改一个参数:1 小时 → 12 小时
最终,我没有修改 Redis,没有调整 PHP-FPM max_children,也没有开启 Cache Preload。
只调整了:
Performance → Page Cache → Advanced → 缓存对象的最长生命周期
修改前:
3600 秒
也就是:1 小时。

随后将它修改为:
43200 秒
也就是:
12 小时。

其他配置保持不变:
Maximum lifetime:
43200 秒
Garbage Collection Interval:
3600 秒
Cache Preload:
关闭
Cache Query String:
关闭
这样做的目的并不是宣称“12 小时一定是最佳值”。
而是先做一个简单、风险较低、容易验证的调整:
降低同一个历史页面反复重新进入高成本动态生成的概率。
十七、保存以后,没有点击“清空所有缓存”
这一步我认为非常重要。
修改完 W3TC 设置以后,后台旁边就有:
清空所有缓存
按钮。
但这一次我没有点击。
原因恰恰来自这次排查最大的发现:
Cold MISS:10.6 秒
Cache HIT:0.016 秒
现在磁盘上已经有 7000 多个缓存文件。
如果为了让配置“重新开始”而主动 Purge All:
大量已有热缓存消失
↓
爬虫重新遍历
↓
大量 URL Cold MISS
↓
WordPress 动态生成
↓
1 vCPU 再次承受缓存回暖压力
这与本轮优化目标正好相反。
所以修改完成以后,我选择:
保留现有缓存,让新生命周期配置在自然运行过程中逐渐生效。
十八、保存后出现 Yoast SEO 扩展提示,也暂时忽略
保存设置以后,W3TC 后台出现提示:
Activating the Yoast SEO extension for W3 Total Cache may be helpful for your site.
我没有在这个阶段启用。
原因并不是认为这个扩展有问题,而是现在正在观察:
Maximum lifetime
3600 → 43200
这个单一调整是否有效。
如果同时继续修改:
Yoast SEO Extension
Redis
PHP-FPM
Cache Preload
CDN
那么下一次 CPU 告警发生变化以后,就很难判断到底是哪一个因素造成的。
所以这一阶段继续遵循:
一次只改一个变量。
十九、这次排查改变了我对几个问题的理解
CPU 满载不一定需要非常高的请求量
如果服务器只有 1 vCPU,而一个 Cold WordPress 页面需要 10 秒,那么几个并发动态请求就已经足够危险。
max_children 达到上限,不等于应该提高它
这次 PHP-FPM 明确提示:
server reached max_children setting (7)
但是只有 1 vCPU。
增加更多 Worker,并不会凭空获得更多 CPU。
Redis Slowlog 慢,不代表 Redis 一定是根因
Redis 确实出现过:
10ms
20ms
50ms
80ms
级别的慢命令。
但服务器只有一个 CPU 核,而 PHP-FPM 正在满负荷争抢这个核心。
Redis 也可能只是被整个系统的 CPU 饱和拖慢。
真正重要的是尽量避免进入 PHP
一次 W3TC HIT:
约 0.016 秒
一次 Cold MISS:
10.6 秒
这个数量级差距比继续调整几个 PHP 参数重要得多。
二十、上一篇 CDN 优化并没有白做
这次排查也让我更清楚地理解了前一天对详情页 Query String 的 CDN 优化。
上一篇解决的是:
同一文章
+
无意义参数
+
缓存复用不足
它可以减少其中一部分进入源站的请求。
而这一次暴露的则是更大的范围:
/tag/
/category/
/page/
Feed
搜索
REST API
各种历史文章
以及大量不同 Cold URL
所以:
详情页 CDN 优化是有效的一层,但它不可能单独解决全部 CPU 告警。
如果后续观察发现 CPU 告警明显减少,就说明 CDN 和 W3TC 两层优化开始产生叠加效果。
二十一、当前生产配置
这一轮结束以后,我最终只改变:
W3 Total Cache
Page Cache
Maximum lifetime
3600 秒
↓
43200 秒
也就是:
1 小时 → 12 小时。
以下项目暂时保持:
Garbage Collection Interval:3600 秒
Cache Preload:关闭
Cache Query String:关闭
PHP-FPM max_children:7
Redis Object Cache:继续启用
Yoast SEO W3TC Extension:暂不启用
同时:
不手工清空已有 Page Cache。
二十二、下一阶段怎么判断是否有效
这一次我不准备继续立即修改。
真正有意义的是让:
43200 秒
实际运行一段时间。
后续重点观察:
CPU 告警是否减少;
单次接近 100% 的持续时间是否缩短;
PHP-FPM 是否仍然频繁:
reached max_children setting (7)
下一次告警期间,Nginx 是否还会再次出现数百个 499;
以及随着 Page Cache 越来越热,Cold MISS 的比例是否逐渐下降。
如果这些指标明显改善,就说明:
延长已经生成缓存的有效周期,是正确方向。
如果没有明显效果,再进一步考虑:
Tag / Category / Page 的 CDN 缓存策略
低价值爬虫访问控制
Cold 页面为什么需要 10 秒才能生成
Gutenberg Navigation / taxonomy 的动态生成成本
而不是现在一次性全部修改。
结语
这次排查很有意思。
最开始,我以为只是上一篇文章中 Query String 缓存问题没有完全解决。
随后一路怀疑:
Nginx
PHP-FPM
Gutenberg
Polylang
W3 Total Cache
Redis
其中 Redis Slowlog 确实出现了大量异常延迟,PHP Slowlog 也有约 73% 的采样落在 W3TC Redis GET/MGET 上。
但真正让我重新理解整个问题的是:
nproc = 1
再结合:
/page/9/
Cold MISS:
10.602379 秒
Cache HIT:
0.016091 秒
答案终于变得简单了。
现在这台 WordPress 服务器真正稀缺的资源,是:
唯一的一个 CPU 核。
因此,相比让更多 PHP-FPM Worker 同时工作,我更希望:
请求根本不要进入 PHP。
CDN、W3TC Disk Enhanced、延长 Page Cache 生命周期,本质上都是在做同一件事:
让已经生成过的页面尽可能直接返回缓存结果。
这一次,我先把 W3TC Page Cache 的最长生命周期从:
1 小时
提高到:
12 小时。
至于它能不能真正降低 CPU 告警,就继续交给生产环境的数据来验证。
WordPress 网站维护、性能优化与博客运营咨询
本站已持续运营超过 10 年,累计发布 1000+ 篇原创技术文章,长期实践 WordPress 网站建设、CDN / Cloudflare 配置、缓存优化、Google SEO、广告变现和多语言网站运营。
如果你的 WordPress 网站遇到访问慢、缓存异常、插件冲突、广告不显示、SEO 基础结构混乱、CDN 配置不确定等问题,可以联系我做一次远程技术排查。
适合以下用户:
✅ 个人博客站长
✅ WordPress 网站运营者
✅ 独立开发者与内容创作者
✅ SaaS 产品官网运营团队
✅ 希望优化网站速度与稳定性的站点
服务内容:
✅ WordPress 速度优化
✅ Cloudflare / CDN / 缓存配置排查
✅ 插件冲突与页面异常排查
✅ AdSense 广告显示问题排查
✅ SEO 基础结构检查
✅ 博客运营与商业化咨询
如需了解方案或交流相关问题,请直接联系我,并注明:WordPress 维护咨询。
联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com


发表回复