2026 年 7 月 28 日,我的 WordPress 服务器再次出现 CPU 告警。
最近一段时间,我已经持续排查过多轮 CPU 告警问题,但这一次我没有继续追究“到底是哪一个进程在那一刻把 CPU 拉高”,而是决定先从另一个方向入手:
重新启用 W3 Total Cache 的页面预缓存,并尽量减少真实用户访问冷页面时触发 PHP 动态生成页面的次数。
第一阶段只是非常保守地开启预缓存。运行一段时间以后,我主观上已经能够明显感觉到 CPU 告警频率下降。
这并不能严格证明“CPU 告警减少就是预缓存直接造成的”,但至少说明目前的缓存调整没有带来新的明显负担,而且值得继续沿着这个方向优化。
最终,我将缓存架构调整为:
用户
│
┌────────┴────────┐
↓ ↓
EdgeOne Cloudflare
www en
96h 96h
│ │
└────────┬────────┘
↓
W3TC
96h
↑
Cache Preload
300 秒 × 7
↑
w3tc-preload-sitemap.xml
www + en 联合列表
↑
每小时自动重新生成
本文记录这次从最初的 900 秒 × 5 页,逐步调整到中英文双域名联合预缓存的完整过程。
一、为什么重新开启 W3 Total Cache 预缓存
W3 Total Cache 的 Page Cache 能够把 WordPress 动态生成的 HTML 页面保存下来。
如果页面已经存在有效缓存,那么后续访问就不需要每次重新经过完整的:
Nginx
→ PHP-FPM
→ WordPress
→ 插件
→ 数据库
→ HTML 输出
但是存在一个问题:
页面缓存通常要等第一次访问以后才能建立。
对于已经有大量历史文章的网站,如果很多缓存因为过期、清理等原因失效,那么某一段时间突然出现大量冷页面访问,就可能集中触发 PHP 页面生成。
W3 Total Cache 的 Cache Preload 正是用来提前生成这些页面缓存的。
W3 Total Cache 官方支持文档中也说明,Cache Preload 会根据 XML Sitemap 主动生成页面缓存,其中:
Update Interval:两批预缓存之间的等待时间;Pages per interval:每批处理的页面数量;Sitemap URL:预缓存所依据的 Sitemap。
默认值分别为 900 秒和每批 10 页。(BoldGrid)
二、第一阶段:先用 900 秒 × 5 页保守运行
重新启用之前,我的 Cache Preload 是关闭状态:
Automatically prime the page cache:关闭
Update interval:900 秒
Pages per interval:10
Sitemap URL:空

因为服务器刚刚发生 CPU 告警,所以我没有直接使用默认的每批 10 页,而是先设置:
Automatically prime the page cache:开启
Update interval:
900 秒
Pages per interval:
5
Sitemap URL:
https://www.shuijingwanwq.com/sitemap_index.xml
同时保持下面两个选项关闭:
Preload cache upon publishing a post:关闭
Preload cache upon updating a post:关闭

这样理论上:
每 15 分钟:5 页
每小时:20 页
每天:480 页
目的不是快速预热整个网站,而是先验证:
在不给服务器增加明显瞬时压力的情况下,持续建立 Page Cache 是否能够改善 CPU 告警情况。
运行一段时间以后,我实际观察到 CPU 告警频率明显降低,于是决定继续优化。
三、12 小时缓存生命周期暴露出的新问题
当时 W3 Total Cache:
Maximum lifetime of cache objects:
43200 秒
也就是:
12 小时
但预缓存只有:
20 页 / 小时
那么 12 小时理论上只能处理:
20 × 12
= 240 页
而网站实际远不止 240 个 URL。
这就让我开始思考:
如果整个 Sitemap 有数千个页面,页面缓存只有 12 小时,而预缓存完整跑一轮却需要好几天,那么是不是应该适当延长缓存生命周期?
需要注意的是,W3TC 并不是每过 12 小时就重新从 Sitemap 第一页开始。
预缓存会继续往后处理。
所以真正的问题不是:
永远只缓存前 240 页
而是:
前面建立的部分缓存已经自然过期
↓
预缓存任务还在继续扫描后面的 URL
↓
还没有重新轮到前面的 URL
这让我决定进一步统计 Sitemap 的真实规模。
四、第一次统计出了 13847 个 URL,但其实是错的
最开始对两个 Sitemap 递归统计以后,得到了:
www:10696
en :4428
合并去重:
13847
更奇怪的是域名分布:
www Sitemap:
7565 media.shuijingwanwq.com
3131 www.shuijingwanwq.com
显然不对。
后来发现问题出在 XML Sitemap 中不只有:
<url>
<loc>https://www.shuijingwanwq.com/...</loc>
</url>
还存在图片信息:
<image:image>
<image:loc>https://media.shuijingwanwq.com/...</image:loc>
</image:image>
第一版统计代码把所有名称为 loc 的 XML 元素全部统计了进去,于是:
image:loc
也被错误地当成了网页 URL。
实际上,media.shuijingwanwq.com 出现在文章 Sitemap 的图片信息中是正常的。
搜索引擎支持 Sitemap 中引用独立域名或 CDN 上的图片,因此:
文章:
www.shuijingwanwq.com
图片:
media.shuijingwanwq.com
本身并不是 SEO 异常。
我们真正需要预热的是 HTML 页面,而不是这些图片 URL。
五、修正统计以后:真正需要处理的是 6270 个页面
统计逻辑调整为:
只读取每个
<url>元素的直接<loc>,忽略image:loc。
最终结果变成:
========== www ==========
www 最终页面 URL:3130
域名分布:
3130 www.shuijingwanwq.com
========== en ==========
en 最终页面 URL:3140
域名分布:
3140 en.shuijingwanwq.com
========== 最终汇总 ==========
www:3130
en :3140
合计:6270
合并去重:6270
这个数字就合理多了。
也就是说,目前真正需要考虑的规模是:
中文站:3130
英文站:3140
总计:
6270 个页面
六、为什么不能继续只使用 www 的 sitemap_index.xml
此时还有另一个问题。
网站已经采用两个语言域名:
中文:
https://www.shuijingwanwq.com/sitemap_index.xml
英文:
https://en.shuijingwanwq.com/sitemap_index.xml
但是 W3 Total Cache 的 Cache Preload 配置只有一个:
Sitemap URL
因此,如果继续填写:
https://www.shuijingwanwq.com/sitemap_index.xml
那么我们设计的预缓存体系实际上只覆盖中文站。
为避免依赖跨域嵌套 Sitemap 的兼容性,我最后没有让 W3TC 自己处理两个 Sitemap Index,而是建立了一个专门供 W3TC 使用的扁平联合 Sitemap:
https://www.shuijingwanwq.com/w3tc-preload-sitemap.xml
里面直接包含:
3130 个 www 页面
+
3140 个 en 页面
而且中文和英文 URL 交替排列:
www URL 1
en URL 1
www URL 2
en URL 2
www URL 3
en URL 3
...
这样就不会出现一轮预缓存开始以后,连续很长时间只处理中文站,然后才轮到英文站的情况。
七、联合 Sitemap 不能做成固定文件
如果今天生成一次:
w3tc-preload-sitemap.xml
以后继续:
- 发布中文文章;
- 发布英文翻译;
- 新增分类;
- 新增标签;
- 新增系列;
- 删除内容;
那么这个联合 Sitemap 很快就会过期。
所以最终方案是:
www Yoast Sitemap ──┐
├── 自动读取
en Yoast Sitemap ───┘
↓
重新生成联合 Sitemap
↓
w3tc-preload-sitemap.xml
↓
W3TC Preload
联合 Sitemap 每小时重新生成一次。
八、直接读取源站 Sitemap,而不是通过 CDN 获取
这里我又做了一层处理。
生成脚本并不是直接访问公网 CDN,而是:
--resolve www.shuijingwanwq.com:443:127.0.0.1
--resolve en.shuijingwanwq.com:443:127.0.0.1
也就是说:
脚本
↓
ECS 本机 Nginx
↓
最新 Yoast Sitemap
不经过:
EdgeOne
Cloudflare
这样即使后面把 CDN TTL 延长到 4 天,联合 Sitemap 的生成任务仍然读取的是源站最新 Sitemap,而不是 CDN 上可能仍然存在的旧版本。
第一次正式生成结果:
www : 3130
en : 3140
total : 6270
output:
/data/wwwroot/www.shuijingwanwq.com/w3tc-preload-sitemap.xml
size : 546232 bytes
status: OK
进一步验证:
总 URL: 6270
唯一 URL: 6270
en.shuijingwanwq.com: 3140
www.shuijingwanwq.com: 3130
源站和公网访问均返回:
HTTP=200
content_type=text/xml
size=546232
九、每小时自动同步,并使用 flock 防止任务重叠
接下来创建:
/etc/cron.d/swq-w3tc-preload-sitemap
内容为:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
17 * * * * root /usr/bin/flock -n /run/lock/swq-w3tc-preload-sitemap.lock /usr/local/bin/swq-w3tc-preload-sitemap.py >> /var/log/swq-w3tc-preload-sitemap.log 2>&1
也就是:
每小时第 17 分钟
↓
获得 flock 锁
↓
读取 www Sitemap
↓
读取 en Sitemap
↓
重新生成联合 Sitemap
↓
验证成功
↓
原子替换旧文件
手动模拟 Cron:
退出码:0
日志:
========== W3TC 联合 Sitemap 生成 ==========
www : 3130
en : 3140
total : 6270
output:
/data/wwwroot/www.shuijingwanwq.com/w3tc-preload-sitemap.xml
size : 546232 bytes
time : 23.77s
status: OK
使用:
flock -n
还有一个好处:
如果上一轮异常执行了很长时间,下一轮不会再启动第二个相同任务。
十、W3 Total Cache 最终改成 96 小时
确定 Sitemap 总量以后,我将:
Maximum lifetime of cache objects
从:
43200 秒
= 12 小时
调整为:
345600 秒
= 96 小时
= 4 天

然后 Cache Preload 从第一阶段的:
900 秒
5 页
www Sitemap
调整成:
Update interval:
300 秒
Pages per interval:
7
Sitemap URL:
https://www.shuijingwanwq.com/w3tc-preload-sitemap.xml

发布和更新文章时的即时预缓存继续关闭:
Preload cache upon publishing a post:关闭
Preload cache upon updating a post:关闭
这样所有后台主动预热都维持一个相对稳定的节奏,而不是在我频繁编辑历史文章的时候又额外触发大量任务。
十一、为什么最终是 300 秒 × 7 页
现在:
每 5 分钟:
7 页
每小时:
7 × 12
= 84 页
每天:
84 × 24
= 2016 页
6270 个 URL 完整走一轮理论需要:
6270 ÷ 84
≈ 74.64 小时
也就是:
约 3.11 天
而 W3TC Page Cache 生命周期为:
96 小时
= 4 天
理论余量大约:
96 - 74.64
≈ 21.36 小时
相比一次生成几十页,我更倾向:
每次少生成一些,但运行得更均匀。
因为本次优化的首要目标本来就是减少 CPU 峰值,而不是为了追求最快速度完成全站预热,又人为制造另一个 CPU 峰值来源。
十二、8064 个 URL 并不是一个必须调参的硬上限
按照:
300 秒 × 7
96 小时理论处理能力为:
84 × 96
= 8064 URL
但后来我意识到:
8064 并不是 URL 数量超过以后就必须立即提高预缓存速度的硬上限。
假设以后 Sitemap 增长到:
10000 URL
完整走一轮需要:
10000 ÷ 84
≈ 119 小时
≈ 5 天
已经超过 96 小时。
但真实网站并不是完全依赖 Preload 来建立缓存。
实际环境还有:
真实用户访问
搜索引擎抓取
CDN 缓存
已有 W3TC Page Cache
Cache Preload
共同参与。
所以即使 Sitemap 达到 10000,也不意味着其中大量页面都会同时处于冷缓存状态。
更重要的是:
热门页面本来就更容易被真实请求重新建立缓存,而极少有人访问的冷门页面,也没有必要为了让它们始终处于“理论热缓存”状态而显著提高服务器负载。
因此以后是否需要从:
300 × 7
继续增加,我不会单纯根据 Sitemap URL 数量判断。
真正应该看的还是:
- CPU 是否稳定;
- CPU 告警频率;
- PHP-FPM 实际负载;
- CDN 回源情况;
- Page Cache 实际命中情况。
十三、EdgeOne 的 www 也延长到 4 天
W3TC 调整完成以后,我将中文站:
www.shuijingwanwq.com
使用的 EdgeOne HTML 页面缓存 TTL 同样从原来的 12 小时调整为:
4 天
= 96 小时
规则仍然排除了动态和后台入口,例如:
wp-admin
wp-login.php
wp-json
feed
xmlrpc.php
wp-cron.php
最终:
节点缓存 TTL:
自定义时间
时间:
4 天
强制缓存:
关闭

EdgeOne 官方说明,节点缓存 TTL 会决定资源在节点缓存中的持续时间;缓存命中可以减少回源请求并降低源站负担。官方同时提醒,EdgeOne 存在冷文件淘汰机制,因此 TTL 是最大缓存周期,并不意味着所有冷门文件一定会在每个节点保存满 4 天。(腾讯云)
这也进一步说明:
缓存设计没必要追求所有 6000 多个页面在任何时间、任何节点都绝对处于热状态。
我们真正追求的是降低总体回源比例和突发回源压力。
十四、Cloudflare 的 en 同样设置为 4 天
英文站:
en.shuijingwanwq.com
使用 Cloudflare。
对应 HTML Cache Rule 中:
缓存资格:
符合缓存条件
Edge TTL 设置为:
忽略缓存控制标头,使用此 TTL
4 天

Cloudflare 官方将 Edge TTL 定义为资源在 Cloudflare 边缘网络中缓存的时间,可以通过 Cache Rules 覆盖源站 TTL。(Cloudflare Docs)
这里修改的是 Edge TTL,并不是浏览器缓存 TTL。
因此:
Cloudflare 节点缓存 HTML 4 天
并不等于:
用户 Firefox / Chrome
必须把 HTML 保存 4 天
这是两个不同的缓存层。
十五、最终缓存架构
至此,整个缓存体系变成:
用户请求
│
┌──────────┴──────────┐
↓ ↓
EdgeOne Cloudflare
www en
96h 96h
│ │
└──────────┬──────────┘
↓
Nginx
↓
W3 Total Cache
96h
↑
Cache Preload
300 秒 × 7
↑
W3TC 联合 Sitemap
6270 URL
↑
┌────────────┴────────────┐
↓ ↓
www Yoast Sitemap en Yoast Sitemap
每小时第 17 分钟自动同步
这套方案的核心不是“缓存越长越好”,而是建立三层防线:
第一层:
CDN 尽量直接 HIT
第二层:
CDN 回源以后尽量 HIT W3TC
第三层:
只有两层都没有可用缓存时
才真正让 PHP + WordPress 动态生成页面
同时通过低批量、高频率的预缓存,让冷页面逐步进入 Page Cache。
十六、这次为什么没有清空已有缓存
完成整个调整以后,我没有执行:
W3TC Purge All Caches
Cloudflare Purge Everything
EdgeOne 全站刷新
因为这次的目标本来就是:
减少冷缓存。
如果刚把预缓存体系搭建完成,就立即清掉现有全部缓存,相当于人为制造一次:
CDN 大量 MISS
+
W3TC 大量 MISS
+
PHP 集中生成页面
这与本次优化方向完全相反。
所以配置调整完成后,我选择让新规则自然运行。
十七、96 小时缓存带来的另一个问题:内容更新后的刷新
缓存时间从 12 小时提高到 4 天以后,还有一个必须意识到的代价:
缓存越长,旧内容在 CDN 节点继续存在的潜在时间也越长。
EdgeOne 官方明确说明,如果源站资源发生更新、需要立即更新节点缓存,可以主动执行缓存清除。(腾讯云)
Cloudflare 同样把 Edge TTL 定义为边缘节点缓存生命周期;设置覆盖源站 TTL 后,就需要特别关注内容更新时的 CDN 清理机制。(Cloudflare Docs)
因此这套 96 小时方案并不代表:
以后发布文章、修改文章都不用考虑缓存刷新
恰恰相反。
对于 WordPress 页面来说,后续还需要继续保证:
内容修改
↓
W3TC 对应页面缓存失效
↓
CDN 对应 URL 也能及时失效或刷新
不过这是“内容更新后的定向缓存刷新”问题,与本文这次解决的“减少随机冷页面造成的源站压力”是两个不同的问题。
本轮调整先不继续扩大范围。
十八、阶段性结论
这次调整最初其实很简单:
CPU 再次告警以后,我决定暂时不继续分析告警根因,而是重新打开 W3 Total Cache 的 Cache Preload。
第一阶段:
W3TC TTL:12 小时
Preload:900 秒 × 5
仅 www
运行以后,我实际观察到 CPU 告警频率有所下降。
于是进一步整理 Sitemap、修正 image:loc 统计问题,并最终实现:
www:3130
en:3140
联合:
6270 URL
再通过自动脚本和 Cron 每小时动态同步:
w3tc-preload-sitemap.xml
最终缓存策略调整为:
W3TC Page Cache:
96 小时
W3TC Cache Preload:
300 秒 × 7
EdgeOne:www
96 小时
Cloudflare:en
96 小时
这次最大的变化并不是单纯把 TTL 从:
12 小时
提高到了:
96 小时
而是把原本相对被动的:
用户访问
→ 页面冷缓存
→ WordPress 临时生成
逐步调整成:
后台持续低速预热
+
CDN 长周期缓存
+
W3TC 长周期 Page Cache
+
真实访问补充热缓存
我现在也不再追求一个理论上的目标:
所有 URL 必须永远处于热缓存。
只要最终能够做到:
大部分真实请求尽量在 CDN 或 W3TC 层结束,同时让主动预缓存保持低强度、稳定运行,不再为了缓存本身制造新的 CPU 峰值。
那么这套方案就已经达到了目的。
接下来我准备暂时不再继续调整参数,让这套:
CDN 96h
+
W3TC 96h
+
300 秒 × 7
+
www/en 联合预缓存
正常运行几天,再根据 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


发表回复