没有不值得去解决的问题,也没有不值得去学习的技术!

从 CPU 告警到双域名预缓存:W3 Total Cache、EdgeOne 与 Cloudflare 96 小时缓存优化实战

图4:W3TC 最终使用 300 秒 × 7 页,并切换到中英文联合 Sitemap

作者:

WordPress 性能优化手记

如 如图1 所示,站点健康直接提示这是一个可能对性能或安全性产生重大影响的问题,需要优先解决。

(1) 从站点健康警告到全绿通关

PHP Fatal error: Uncaught RedisException: OOM command not allowed when used memory > 'maxmemory'. in /data/wwwroot/.../wp-content/plugins/w3-total-cache/Cache_Redis.php:150

(2) 解决 WordPress + Polylang 批量处理标签时遇到的 Redis OOM 错误

采用 ondemand 模式,适合 1 核小内存机器,空闲时释放进程。最终配置如下:如图4

(3) 一次WordPress站点504错误的排查与优化实录

优化前最终基准数据:历史累计504错误129条(6月3日峰值126条,为爬虫批量爬标签导致)。

(4) 从单日126次504超时到彻底稳定:WordPress 1核2G服务器极限优化全记录(附全部实操命令)

WebPageTest 核心指标

(5) WordPress性能基准测试与CDN选型实录:从国内拨测到海外WebPageTest,1核2G服务器如何面向全球

页面底部提示“此响应不是合法的 JSON 响应”。

(6) WordPress 标签保存失败?Nginx 限流规则惹的祸 —— 一次完整的 429 问题排查与解决

表示页面仍然是动态生成,没有缓存命中。

(7) WordPress + Nginx + W3 Total Cache 缓存未生效排查全过程(OneinStack 实战)

图7:带 Query String 后,W3TC 显示 Requested URI contains query

(8) WordPress CPU 再次满载:动态参数如何穿透 CDN 与 W3TC 页面缓存

图1:阿里云 ECS 在 14:33~14:42 期间 CPU 使用率持续接近 100%,而内存使用率整体较为稳定

(9) WordPress 服务器 CPU 再次满载:从 Nginx 499、PHP-FPM、Redis 到 W3TC 冷缓存的完整排查

图4:W3TC 最终使用 300 秒 × 7 页,并切换到中英文联合 Sitemap

(10) 从 CPU 告警到双域名预缓存:W3 Total Cache、EdgeOne 与 Cloudflare 96 小时缓存优化实战

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 页面保存下来。

如果页面已经存在有效缓存,那么后续访问就不需要每次重新经过完整的:

Plaintext
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 是关闭状态:

Plaintext
Automatically prime the page cache:关闭
Update interval:900 秒
Pages per interval:10
Sitemap URL:空
图1:重新启用前,W3 Total Cache 的 Cache Preload 处于关闭状态
图1:重新启用前,W3 Total Cache 的 Cache Preload 处于关闭状态

因为服务器刚刚发生 CPU 告警,所以我没有直接使用默认的每批 10 页,而是先设置:

Plaintext
Automatically prime the page cache:开启

Update interval:
900 秒

Pages per interval:
5

Sitemap URL:
https://www.shuijingwanwq.com/sitemap_index.xml

同时保持下面两个选项关闭:

Plaintext
Preload cache upon publishing a post:关闭

Preload cache upon updating a post:关闭
图2:第一阶段采用 900 秒 × 5 页的低强度预缓存
图2:第一阶段采用 900 秒 × 5 页的低强度预缓存

这样理论上:

Plaintext
每 15 分钟:5 页
每小时:20 页
每天:480 页

目的不是快速预热整个网站,而是先验证:

在不给服务器增加明显瞬时压力的情况下,持续建立 Page Cache 是否能够改善 CPU 告警情况。

运行一段时间以后,我实际观察到 CPU 告警频率明显降低,于是决定继续优化。


三、12 小时缓存生命周期暴露出的新问题

当时 W3 Total Cache:

Plaintext
Maximum lifetime of cache objects:
43200 秒

也就是:

Plaintext
12 小时

但预缓存只有:

Plaintext
20 页 / 小时

那么 12 小时理论上只能处理:

Plaintext
20 × 12
= 240 页

而网站实际远不止 240 个 URL。

这就让我开始思考:

如果整个 Sitemap 有数千个页面,页面缓存只有 12 小时,而预缓存完整跑一轮却需要好几天,那么是不是应该适当延长缓存生命周期?

需要注意的是,W3TC 并不是每过 12 小时就重新从 Sitemap 第一页开始。

预缓存会继续往后处理。

所以真正的问题不是:

Plaintext
永远只缓存前 240 页

而是:

Plaintext
前面建立的部分缓存已经自然过期

预缓存任务还在继续扫描后面的 URL

还没有重新轮到前面的 URL

这让我决定进一步统计 Sitemap 的真实规模。


四、第一次统计出了 13847 个 URL,但其实是错的

最开始对两个 Sitemap 递归统计以后,得到了:

Plaintext
www:10696
en :4428

合并去重:
13847

更奇怪的是域名分布:

Plaintext
www Sitemap:

7565  media.shuijingwanwq.com
3131  www.shuijingwanwq.com

显然不对。

后来发现问题出在 XML Sitemap 中不只有:

XML
<url>
    <loc>https://www.shuijingwanwq.com/...</loc>
</url>

还存在图片信息:

XML
<image:image>
    <image:loc>https://media.shuijingwanwq.com/...</image:loc>
</image:image>

第一版统计代码把所有名称为 loc 的 XML 元素全部统计了进去,于是:

Plaintext
image:loc

也被错误地当成了网页 URL。

实际上,media.shuijingwanwq.com 出现在文章 Sitemap 的图片信息中是正常的。

搜索引擎支持 Sitemap 中引用独立域名或 CDN 上的图片,因此:

Plaintext
文章:
www.shuijingwanwq.com

图片:
media.shuijingwanwq.com

本身并不是 SEO 异常。

我们真正需要预热的是 HTML 页面,而不是这些图片 URL。


五、修正统计以后:真正需要处理的是 6270 个页面

统计逻辑调整为:

只读取每个 <url> 元素的直接 <loc>,忽略 image:loc

最终结果变成:

Plaintext
========== www ==========

www 最终页面 URL:3130

域名分布:
3130  www.shuijingwanwq.com


========== en ==========

en 最终页面 URL:3140

域名分布:
3140  en.shuijingwanwq.com


========== 最终汇总 ==========

www:3130
en :3140
合计:6270
合并去重:6270

这个数字就合理多了。

也就是说,目前真正需要考虑的规模是:

Plaintext
中文站:3130
英文站:3140

总计:
6270 个页面

六、为什么不能继续只使用 www 的 sitemap_index.xml

此时还有另一个问题。

网站已经采用两个语言域名:

Plaintext
中文:
https://www.shuijingwanwq.com/sitemap_index.xml

英文:
https://en.shuijingwanwq.com/sitemap_index.xml

但是 W3 Total Cache 的 Cache Preload 配置只有一个:

Plaintext
Sitemap URL

因此,如果继续填写:

Plaintext
https://www.shuijingwanwq.com/sitemap_index.xml

那么我们设计的预缓存体系实际上只覆盖中文站。

为避免依赖跨域嵌套 Sitemap 的兼容性,我最后没有让 W3TC 自己处理两个 Sitemap Index,而是建立了一个专门供 W3TC 使用的扁平联合 Sitemap

Plaintext
https://www.shuijingwanwq.com/w3tc-preload-sitemap.xml

里面直接包含:

Plaintext
3130 个 www 页面
+
3140 个 en 页面

而且中文和英文 URL 交替排列:

Plaintext
www URL 1
en URL 1
www URL 2
en URL 2
www URL 3
en URL 3
...

这样就不会出现一轮预缓存开始以后,连续很长时间只处理中文站,然后才轮到英文站的情况。


七、联合 Sitemap 不能做成固定文件

如果今天生成一次:

Plaintext
w3tc-preload-sitemap.xml

以后继续:

  • 发布中文文章;
  • 发布英文翻译;
  • 新增分类;
  • 新增标签;
  • 新增系列;
  • 删除内容;

那么这个联合 Sitemap 很快就会过期。

所以最终方案是:

Plaintext
www Yoast Sitemap ──┐
                    ├── 自动读取
en Yoast Sitemap ───┘

               重新生成联合 Sitemap

             w3tc-preload-sitemap.xml

                    W3TC Preload

联合 Sitemap 每小时重新生成一次。


八、直接读取源站 Sitemap,而不是通过 CDN 获取

这里我又做了一层处理。

生成脚本并不是直接访问公网 CDN,而是:

Bash
--resolve www.shuijingwanwq.com:443:127.0.0.1
--resolve en.shuijingwanwq.com:443:127.0.0.1

也就是说:

Plaintext
脚本

ECS 本机 Nginx

最新 Yoast Sitemap

不经过:

Plaintext
EdgeOne
Cloudflare

这样即使后面把 CDN TTL 延长到 4 天,联合 Sitemap 的生成任务仍然读取的是源站最新 Sitemap,而不是 CDN 上可能仍然存在的旧版本。

第一次正式生成结果:

Plaintext
www   : 3130
en    : 3140
total : 6270

output:
/data/wwwroot/www.shuijingwanwq.com/w3tc-preload-sitemap.xml

size  : 546232 bytes

status: OK

进一步验证:

Plaintext
总 URL: 6270
唯一 URL: 6270

en.shuijingwanwq.com: 3140
www.shuijingwanwq.com: 3130

源站和公网访问均返回:

Plaintext
HTTP=200
content_type=text/xml
size=546232

九、每小时自动同步,并使用 flock 防止任务重叠

接下来创建:

Plaintext
/etc/cron.d/swq-w3tc-preload-sitemap

内容为:

Bash
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

也就是:

Plaintext
每小时第 17 分钟

获得 flock 锁

读取 www Sitemap

读取 en Sitemap

重新生成联合 Sitemap

验证成功

原子替换旧文件

手动模拟 Cron:

Plaintext
退出码:0

日志:

Plaintext
========== 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

使用:

Plaintext
flock -n

还有一个好处:

如果上一轮异常执行了很长时间,下一轮不会再启动第二个相同任务。


十、W3 Total Cache 最终改成 96 小时

确定 Sitemap 总量以后,我将:

Plaintext
Maximum lifetime of cache objects

从:

Plaintext
43200 秒
= 12 小时

调整为:

Plaintext
345600 秒
= 96 小时
= 4 天
图3:W3 Total Cache 页面缓存最长生命周期调整为 345600 秒,即 4 天
图3:W3 Total Cache 页面缓存最长生命周期调整为 345600 秒,即 4 天

然后 Cache Preload 从第一阶段的:

Plaintext
900 秒
5 页
www Sitemap

调整成:

Plaintext
Update interval:
300 秒

Pages per interval:
7

Sitemap URL:
https://www.shuijingwanwq.com/w3tc-preload-sitemap.xml
图4:W3TC 最终使用 300 秒 × 7 页,并切换到中英文联合 Sitemap
图4:W3TC 最终使用 300 秒 × 7 页,并切换到中英文联合 Sitemap

发布和更新文章时的即时预缓存继续关闭:

Plaintext
Preload cache upon publishing a post:关闭
Preload cache upon updating a post:关闭

这样所有后台主动预热都维持一个相对稳定的节奏,而不是在我频繁编辑历史文章的时候又额外触发大量任务。


十一、为什么最终是 300 秒 × 7 页

现在:

Plaintext
每 5 分钟:
7 页

每小时:
7 × 12
= 84 页

每天:
84 × 24
= 2016 页

6270 个 URL 完整走一轮理论需要:

Plaintext
6270 ÷ 84
≈ 74.64 小时

也就是:

Plaintext
约 3.11 天

而 W3TC Page Cache 生命周期为:

Plaintext
96 小时
= 4 天

理论余量大约:

Plaintext
96 - 74.64
≈ 21.36 小时

相比一次生成几十页,我更倾向:

每次少生成一些,但运行得更均匀。

因为本次优化的首要目标本来就是减少 CPU 峰值,而不是为了追求最快速度完成全站预热,又人为制造另一个 CPU 峰值来源。


十二、8064 个 URL 并不是一个必须调参的硬上限

按照:

Plaintext
300 秒 × 7

96 小时理论处理能力为:

Plaintext
84 × 96
= 8064 URL

但后来我意识到:

8064 并不是 URL 数量超过以后就必须立即提高预缓存速度的硬上限。

假设以后 Sitemap 增长到:

Plaintext
10000 URL

完整走一轮需要:

Plaintext
10000 ÷ 84
≈ 119 小时
≈ 5 天

已经超过 96 小时。

但真实网站并不是完全依赖 Preload 来建立缓存。

实际环境还有:

Plaintext
真实用户访问
搜索引擎抓取
CDN 缓存
已有 W3TC Page Cache
Cache Preload

共同参与。

所以即使 Sitemap 达到 10000,也不意味着其中大量页面都会同时处于冷缓存状态。

更重要的是:

热门页面本来就更容易被真实请求重新建立缓存,而极少有人访问的冷门页面,也没有必要为了让它们始终处于“理论热缓存”状态而显著提高服务器负载。

因此以后是否需要从:

Plaintext
300 × 7

继续增加,我不会单纯根据 Sitemap URL 数量判断。

真正应该看的还是:

  • CPU 是否稳定;
  • CPU 告警频率;
  • PHP-FPM 实际负载;
  • CDN 回源情况;
  • Page Cache 实际命中情况。

十三、EdgeOne 的 www 也延长到 4 天

W3TC 调整完成以后,我将中文站:

Plaintext
www.shuijingwanwq.com

使用的 EdgeOne HTML 页面缓存 TTL 同样从原来的 12 小时调整为:

Plaintext
4 天
= 96 小时

规则仍然排除了动态和后台入口,例如:

Plaintext
wp-admin
wp-login.php
wp-json
feed
xmlrpc.php
wp-cron.php

最终:

Plaintext
节点缓存 TTL:
自定义时间

时间:
4 天

强制缓存:
关闭
图5:EdgeOne HTML 页面节点缓存 TTL 调整为 4 天
图5:EdgeOne HTML 页面节点缓存 TTL 调整为 4 天

EdgeOne 官方说明,节点缓存 TTL 会决定资源在节点缓存中的持续时间;缓存命中可以减少回源请求并降低源站负担。官方同时提醒,EdgeOne 存在冷文件淘汰机制,因此 TTL 是最大缓存周期,并不意味着所有冷门文件一定会在每个节点保存满 4 天。(腾讯云)

这也进一步说明:

缓存设计没必要追求所有 6000 多个页面在任何时间、任何节点都绝对处于热状态。

我们真正追求的是降低总体回源比例和突发回源压力。


十四、Cloudflare 的 en 同样设置为 4 天

英文站:

Plaintext
en.shuijingwanwq.com

使用 Cloudflare。

对应 HTML Cache Rule 中:

Plaintext
缓存资格:
符合缓存条件

Edge TTL 设置为:

Plaintext
忽略缓存控制标头,使用此 TTL

4 天
图6:Cloudflare 英文站 HTML Edge TTL 调整为 4 天
图6:Cloudflare 英文站 HTML Edge TTL 调整为 4 天

Cloudflare 官方将 Edge TTL 定义为资源在 Cloudflare 边缘网络中缓存的时间,可以通过 Cache Rules 覆盖源站 TTL。(Cloudflare Docs)

这里修改的是 Edge TTL,并不是浏览器缓存 TTL。

因此:

Plaintext
Cloudflare 节点缓存 HTML 4 天

并不等于:

Plaintext
用户 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 分钟自动同步

这套方案的核心不是“缓存越长越好”,而是建立三层防线:

Plaintext
第一层:
CDN 尽量直接 HIT

第二层:
CDN 回源以后尽量 HIT W3TC

第三层:
只有两层都没有可用缓存时
才真正让 PHP + WordPress 动态生成页面

同时通过低批量、高频率的预缓存,让冷页面逐步进入 Page Cache。


十六、这次为什么没有清空已有缓存

完成整个调整以后,我没有执行:

Plaintext
W3TC Purge All Caches
Cloudflare Purge Everything
EdgeOne 全站刷新

因为这次的目标本来就是:

减少冷缓存。

如果刚把预缓存体系搭建完成,就立即清掉现有全部缓存,相当于人为制造一次:

Plaintext
CDN 大量 MISS
+
W3TC 大量 MISS
+
PHP 集中生成页面

这与本次优化方向完全相反。

所以配置调整完成后,我选择让新规则自然运行。


十七、96 小时缓存带来的另一个问题:内容更新后的刷新

缓存时间从 12 小时提高到 4 天以后,还有一个必须意识到的代价:

缓存越长,旧内容在 CDN 节点继续存在的潜在时间也越长。

EdgeOne 官方明确说明,如果源站资源发生更新、需要立即更新节点缓存,可以主动执行缓存清除。(腾讯云)

Cloudflare 同样把 Edge TTL 定义为边缘节点缓存生命周期;设置覆盖源站 TTL 后,就需要特别关注内容更新时的 CDN 清理机制。(Cloudflare Docs)

因此这套 96 小时方案并不代表:

Plaintext
以后发布文章、修改文章都不用考虑缓存刷新

恰恰相反。

对于 WordPress 页面来说,后续还需要继续保证:

Plaintext
内容修改

W3TC 对应页面缓存失效

CDN 对应 URL 也能及时失效或刷新

不过这是“内容更新后的定向缓存刷新”问题,与本文这次解决的“减少随机冷页面造成的源站压力”是两个不同的问题。

本轮调整先不继续扩大范围。


十八、阶段性结论

这次调整最初其实很简单:

CPU 再次告警以后,我决定暂时不继续分析告警根因,而是重新打开 W3 Total Cache 的 Cache Preload。

第一阶段:

Plaintext
W3TC TTL:12 小时
Preload:900 秒 × 5
仅 www

运行以后,我实际观察到 CPU 告警频率有所下降。

于是进一步整理 Sitemap、修正 image:loc 统计问题,并最终实现:

Plaintext
www:3130
en:3140

联合:
6270 URL

再通过自动脚本和 Cron 每小时动态同步:

Plaintext
w3tc-preload-sitemap.xml

最终缓存策略调整为:

Plaintext
W3TC Page Cache:
96 小时

W3TC Cache Preload:
300 秒 × 7

EdgeOne:www
96 小时

Cloudflare:en
96 小时

这次最大的变化并不是单纯把 TTL 从:

Plaintext
12 小时

提高到了:

Plaintext
96 小时

而是把原本相对被动的:

Plaintext
用户访问
→ 页面冷缓存
→ WordPress 临时生成

逐步调整成:

Plaintext
后台持续低速预热
+
CDN 长周期缓存
+
W3TC 长周期 Page Cache
+
真实访问补充热缓存

我现在也不再追求一个理论上的目标:

所有 URL 必须永远处于热缓存。

只要最终能够做到:

大部分真实请求尽量在 CDN 或 W3TC 层结束,同时让主动预缓存保持低强度、稳定运行,不再为了缓存本身制造新的 CPU 峰值。

那么这套方案就已经达到了目的。

接下来我准备暂时不再继续调整参数,让这套:

Plaintext
CDN 96h
+
W3TC 96h
+
300 秒 × 7
+
www/en 联合预缓存

正常运行几天,再根据 CPU 告警频率和实际回源情况判断是否还有继续优化的必要。

WordPress 服务器 CPU 再次满载:从 Nginx 499、PHP-FPM、Redis 到 W3TC 冷缓存的完整排查

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理