上一篇记录了这次 CPU 告警出现以后,我在 EdgeOne 层看到的异常流量,以及最终在个人版现有能力范围内,对自适应频控、JavaScript 挑战和流量防盗刷所做的调整。
不过,EdgeOne 解决的主要还是流量入口层的问题。
实际上,在这次 CPU 告警之前,我就已经注意到 W3 Total Cache 的页面缓存状态有些不对。
我的 Page Cache 生命周期设置为 4 天,按理说缓存目录里应该能够看到存活 2 天、3 天甚至接近 4 天的页面文件。但之前实际检查时,却发现缓存普遍很新,几乎看不到超过 2 天的页面。
当时没有继续深入排查,是因为那段时间还在批量处理大量历史文章。
文章批量更新本身就可能触发页面缓存失效、首页刷新等操作,因此在那个阶段看到缓存年龄偏短,并不能很好地区分:
到底是 W3 Total Cache 本身有问题,还是历史文章批量处理造成的正常缓存失效。
所以这个问题当时暂时搁置了下来。
现在历史文章的批量处理早已经结束,站点也重新进入相对稳定的运行状态。如果页面缓存仍然长期无法超过 2 天,那就不能再用“文章正在批量更新”来解释了。
正好这一次又遇到服务器 CPU 告警,我便决定把这个遗留问题一起查清楚:
为什么明明设置了 4 天 Page Cache 生命周期,实际缓存却长期看不到超过 2 天的页面?
结果继续往下查以后,先发现了一个已经持续了半个月左右的问题:
W3 Total Cache 用于页面缓存预热的联合 Sitemap,实际上一直生成失败。
继续追下去以后,最终又定位到 Yoast SEO 的 post-sitemap.xml:生成文章 Sitemap 时,PHP-FPM 的 256M 内存上限被耗尽,从而返回 HTTP 500。
这一次就继续记录服务器侧的排查过程。
一、W3TC 联合预热 Sitemap 一直生成失败
我的 WordPress 使用 W3 Total Cache 的 Disk Enhanced 页面缓存,同时开启了页面缓存预热。
为了同时预热中文站和英文站,我之前另外写了一套联合 Sitemap 生成脚本,将:
www.shuijingwanwq.com和:
en.shuijingwanwq.com中的 URL 合并到:
w3tc-preload-sitemap.xml再交给 W3TC Preload 使用。
继续排查时查看生成日志,却发现里面连续出现:
status: FAILED
读取失败:https://www.shuijingwanwq.com/post-sitemap.xml
curl: (22) The requested URL returned error: 500其中还有一次:
curl: (28) Operation timed out after 30002 milliseconds with 0 bytes received
也就是说,联合 Sitemap 长期没有正常更新,并不是 W3TC 自己不会读取 Sitemap。
问题发生得更早:
联合 Sitemap 生成脚本
↓
读取 www 的 post-sitemap.xml
↓
HTTP 500 / 超时
↓
联合 Sitemap 生成失败这也解释了为什么此前 W3TC 的页面预热一直没有获得最新的 URL 列表。
二、先排除 Linux Cron 没有运行
看到一个定时生成文件长期不更新,第一个很自然的怀疑就是:
是不是 Linux Cron 根本没有正常运行?
这套联合 Sitemap 的系统 Cron 配置为每小时第 17 分钟执行一次。
继续查看 /var/log/cron,可以清楚看到:
09:17:01
10:17:01
11:17:01
12:17:01
13:17:01
14:17:01
15:17:01
16:17:01
17:17:01
18:17:01每个小时都在正常触发:
/usr/bin/flock -n /run/lock/swq-w3tc-preload-sitemap.lock
/usr/local/bin/swq-w3tc-preload-sitemap.py
所以这里可以先排除一个方向:
不是定时任务没有运行,而是定时任务运行以后,每次都在读取上游 Sitemap 时失败。
这两种情况的处理方式完全不一样。
如果只是 Cron 没运行,那么修复 Cron 就结束了。
现在的问题显然要继续往 WordPress / PHP 里面追。
三、www 的 post-sitemap.xml 为什么会返回 500?
接下来直接测试:
https://www.shuijingwanwq.com/post-sitemap.xml发现它确实会返回 HTTP 500。
英文站的:
https://en.shuijingwanwq.com/post-sitemap.xml也存在类似问题。
但站点的:
sitemap_index.xml却能够正常访问。
因此问题已经进一步收窄:
并不是 Yoast Sitemap 功能整体失效,而是在真正生成文章 Sitemap 时出现了异常。
继续查看 PHP-FPM 日志以后,很快找到了最关键的一行:
PHP Fatal error:
Allowed memory size of 268435456 bytes exhausted其中:
268435456 bytes正好就是:
256 MB日志中还能看到异常发生在:
wp-includes/class-wpdb.php数据库结果读取过程中。

结合当时完整的调用栈继续向上追,可以看到请求正在进入 Yoast SEO 的文章 Sitemap 生成逻辑,包括:
wpdb->get_results()
WPSEO_Post_Type_Sitemap_Provider->get_posts()因此 post-sitemap.xml 返回 HTTP 500 的直接原因已经可以确定:
Yoast SEO 生成文章 Sitemap 时,单次 PHP 请求超过了当前 256M 的 memory_limit。
四、为什么生成一个 Sitemap 会消耗这么多内存?
这点一开始其实也让我有些意外。
Sitemap 看起来只是生成一批 URL,理论上似乎不应该需要特别大的内存。
但继续看 Yoast 的实现以后,会发现它并不是简单查询:
post ID然后拼接 URL。
生成文章 Sitemap 时,需要读取一定数量的文章数据。
Yoast 默认单个 Sitemap 可以包含大约:
1000 个 URL而我的 WordPress 已经积累了几千篇文章,其中很多还是篇幅比较长的技术文章,包含大量代码、日志和配置内容。
因此一次 Sitemap 请求实际需要处理的数据量,并不算小。
当 PHP-FPM 单请求内存上限只有:
256M时,当前文章规模已经有可能触碰上限。
这次 HTTP 500 实际上就是这个阈值被真正撞到了。
五、php.ini 中还存在两处 memory_limit
继续检查:
/usr/local/php/etc/php.ini又发现一个容易造成误解的地方。
配置文件中实际上存在两处:
memory_limit = 128M和:
memory_limit = 256M修改以前的位置分别大约在:
430
1883其中真正最终生效的是后面的:
256M为了避免以后再因为重复配置产生判断上的混乱,这次没有只改其中一处,而是将两处统一调整为:
memory_limit = 512M修改后再次检查:
430:memory_limit = 512M
1883:memory_limit = 512MPHP-FPM 的实际有效值也变成:
memory_limit => 512M => 512M
这里需要特别说明:
将
memory_limit设置为 512M,并不意味着每一个 PHP-FPM worker 一启动就会固定占用 512M。
它只是单个 PHP 请求允许使用的内存上限。
不过我的服务器总内存并不算多,因此这个调整也不能理解成“内存越大越好”。
更准确地说,是当前 256M 已经通过实际故障证明不够用了,因此先提高到 512M,再继续观察。
如果以后高并发情况下又因为单请求内存过高造成服务器整体内存压力,还需要重新权衡。
六、调整到 512M 后,post-sitemap.xml 恢复正常
修改 php.ini 后,我先检查 PHP-FPM 配置没有语法错误,再 reload PHP-FPM。
随后重新请求中文站:
https://www.shuijingwanwq.com/post-sitemap.xml测试结果:
HTTP: 200
Size: 756668 bytes
Time: 8.156836s再测试英文站:
https://en.shuijingwanwq.com/post-sitemap.xml结果同样恢复:
HTTP: 200
Size: 224628 bytes
Time: 15.314945s
这一步比较关键,因为它不是单纯看到日志不再报错,而是直接验证了原来失败的目标 URL。
修复前:
post-sitemap.xml
→ HTTP 500修复后:
post-sitemap.xml
→ HTTP 200所以至少对于这次 Sitemap 500 来说,256M 内存不足就是直接原因。
七、联合预热 Sitemap 也终于恢复更新
上游的 post-sitemap.xml 恢复以后,联合 Sitemap 生成脚本自然也就能够重新取得完整的 Sitemap 数据。
刚完成修复时,我手工重新生成了一次联合 Sitemap,当时统计到:
www 3321
en 3323
total 6644到了后面准备这篇文章截图时,系统 Cron 又继续自动更新,当前已经变成:
www: 3323
en : 3325
all: 6648
这里反而能够再次验证前面的判断:
Linux Cron 本身一直没有坏。
之前它每小时都执行,只是由于:
post-sitemap.xml = 500所以一直生成失败。
现在上游恢复正常以后,同一套系统 Cron 不需要修改,就可以继续自动更新联合 Sitemap。
截至截图时:
中文站:3323
英文站:3325
总计:6648URL 数量已经继续增长。
八、这意味着此前新增页面没有进入最新预热列表
此前联合 Sitemap 长时间生成失败,还有一个比较直接的影响:
这段时间新增的文章和页面,并没有及时进入最新的 W3TC 联合预热 Sitemap。
此前旧文件中大约有:
www:3197
en :3213
总计:6410而当前已经增长到:
www:3323
en :3325
总计:6648两者相差:
6648 - 6410 = 238也就是说,随着站点继续更新,旧的预热列表与当前实际 URL 数量之间已经产生了明显差距。
这并不意味着这 238 个 URL 一定完全没有页面缓存。
因为用户或者爬虫真实访问页面以后,W3 Total Cache 仍然可以正常生成缓存。
但它们没有进入最新的主动预热列表,就意味着:
不能再依赖 W3TC Preload 主动、持续地提前把这些新增页面缓存起来。
对于正常流量不大的个人博客,这个问题平时可能并不明显。
但是一旦突然出现大量自动化访问,未预热页面第一次请求需要真正执行 WordPress / PHP,就可能增加瞬时服务器压力。
九、当前预热速度仍然能够覆盖 4 天缓存生命周期
W3 Total Cache 当前的 Page Cache 配置中:
缓存生命周期:345600 秒也就是:
4 天Preload 当前设置为:
每 300 秒执行一次
每次预热 7 个 URL换算以后:
每小时:
12 × 7 = 84 个 URL
每天:
84 × 24 = 2016 个 URL按照截图时最新的:
6648 个 URL完整跑完一轮大约需要:
6648 ÷ 2016 ≈ 3.30 天也就是大约:
3 天 7 小时相比:
4 天缓存生命周期理论上仍然保留了大约:
17 小时左右的余量。
因此我暂时没有因为这次问题,就把:
7 URLs / 5 分钟继续提高。
毕竟这台 ECS 只有 2 vCPU。
预热本身也是一次真实的页面请求,如果把速度设置得过于激进,可能反过来制造持续性的 PHP 压力。
现阶段更适合先恢复原有机制,再观察缓存是否能够逐渐形成稳定的 1 天、2 天、3 天生命周期。
十、这次为什么没有直接降低 Yoast 每个 Sitemap 的 URL 数?
既然问题发生在 Yoast 一次生成较大的文章 Sitemap 时,另一个可能的解决方向其实是:
把每个 Sitemap 包含的 URL 数从默认的约 1000 个降低。
这样理论上确实能够减少单次请求需要处理的数据量。
不过这次我没有先走这个方案。
原因是当前 256M 不只是 Sitemap 已经碰到限制。
当天 PHP-FPM 日志中还出现了其他 WordPress / W3TC 请求触碰 256M 内存上限的情况。
这说明随着站点规模增长:
256M 对当前 WordPress 生产环境本身已经开始偏紧。
因此这次先把 PHP-FPM 的单请求上限提高到:
512M更符合当前实际情况。
Yoast 的 Sitemap 分页数量暂时保持默认,不再同时调整多个变量。
如果以后 Sitemap 又开始持续占用异常大的内存,再单独考虑降低每个 Sitemap 的 URL 数量。
十一、问题原来不是“W3TC 预热太慢”
这次排查还有一个挺容易走偏的地方。
最开始发现页面缓存数量和预期不太一致时,很容易怀疑:
是不是 W3TC 每次只预热 7 个 URL 太慢了?于是自然会想到把:
7直接提高到:
10
20
甚至更多但继续查下去才发现,真正的问题根本不是“每批预热数量太少”。
而是:
W3TC Preload
↓
依赖联合 Sitemap
↓
联合 Sitemap 需要读取 Yoast post-sitemap.xml
↓
post-sitemap.xml 因 256M OOM 返回 500
↓
联合 Sitemap 长期生成失败
↓
W3TC 一直拿不到最新 URL 列表如果没有继续查原因,而只是不断提高预热速度,不但解决不了这个问题,还可能进一步增加服务器压力。
这也是为什么性能问题不能只盯着最后一个表现出来的参数。
十二、CPU 告警背后,其实不止一个问题
回到最开始的 CPU 告警。
第一篇已经确认:
短时间大量异常访问确实是触发服务器高负载的重要外部因素。
但继续往源站分析以后,又发现:
W3TC 联合预热 Sitemap 长期失效这是另一个已经存在一段时间的内部问题。
所以这次事故不能简单概括成:
EdgeOne 没有挡住异常流量。
更准确的理解应该是:
外部:
短时间异常自动化流量
↓
EdgeOne 大量回源
内部:
部分页面预热机制长期异常
↓
更多请求需要依赖源站实际缓存状态
↓
PHP-FPM 承受更大压力两边的问题叠加以后,一台只有 2 vCPU 的服务器,自然更容易被推到 100% CPU。
十三、原以为到这里已经结束,结果还有下一层问题
到了这里,当时我原本以为已经可以收尾了。
因为几个关键问题都已经恢复:
Yoast post-sitemap.xml
500 → 200
PHP memory_limit
256M → 512M
联合 Sitemap
恢复自动更新
URL 数量
6410 → 当前 6648接下来只要 W3 Total Cache 按照:
每 5 分钟
预热 7 个 URL继续运行,就应该能够逐渐重新建立完整的页面缓存。
但是继续检查的时候,又发现了一个更加奇怪的问题:
W3TC 的
w3_pgcache_prime和w3_pgcache_cleanup两个 WP-Cron Event,竟然没有正常工作。
更离谱的是:
手工创建 Event
→ 显示成功
执行 wp-cron.php
→ Event 却又消失最后为了验证是不是 W3TC 自己删除了任务,我甚至临时创建了一个完全随机的 Cron Probe。
结果一个安排在两小时以后、任何插件都不认识的新 Event,也会在运行 wp-cron.php 后消失。
继续追下去,最终出现了一组非常关键的结果:
数据库:
DB = YES
WordPress get_option("cron"):
NO也就是说:
MySQL 中已经是新的 Cron 数据,但 WordPress 从 Object Cache 中读到的却还是旧版本。
这个问题最后又牵出了:
WP-Cron
WP-CLI
W3TC Redis Object Cache
alloptions
多域名
Polylang
admin.shuijingwanwq.com之间的关系。
这一部分已经是另外一个相对独立的问题了,所以继续留到下一篇单独记录。
十四、这一次先解决“源站预热入口”
到这里,这一阶段可以先得到一个比较明确的结论。
最开始看到:
W3TC 页面缓存似乎没有达到预期并不代表:
W3TC Disk Enhanced 本身坏了这一次首先发现的问题其实发生在它上游:
Yoast Sitemap
↓
PHP 内存不足
↓
post-sitemap.xml 500
↓
联合 Sitemap 生成失败
↓
预热 URL 列表长期无法更新最终通过:
PHP memory_limit
256M → 512M恢复了中英文站的 post-sitemap.xml,联合 Sitemap 也重新恢复自动更新。
而当前:
6648 URLs按照每 5 分钟预热 7 个 URL 的速度,理论上仍然能够在 4 天 Page Cache 生命周期以内完成一轮预热。
所以这一阶段我没有继续增加预热强度,也没有执行任何全量页面缓存清理。
先让现有缓存自然运行,是现在更稳妥的做法。
至于为什么 WP-Cron Event 后来又会莫名其妙消失,以及最后是怎样通过 DB=YES / get_option=NO 抓到 Redis alloptions 陈旧缓存的,就放到下一篇继续。
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

