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

W3TC 定时任务为何创建后又消失?排查 WP-Cron 与 Redis alloptions 陈旧缓存

图 2:新的 PHP 进程通过 get_option("cron") 读取不到 Probe,但数据库中明确存在

作者:

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 小时缓存优化实战

WordPress 所有归档模板中使用 Language Visibility 和 WPCode Shortcode 添加中英文 Adsterra 广告

(11) WordPress 三域名架构再次踩坑:Adsterra 归档广告不生效,最终定位到 W3TC Object Cache

WordPress 多语言多域名缓存排查:修复 Polylang + W3 Total Cache 跨 Host 缓存失效问题

(12) WordPress 多语言多域名缓存排查:修复 Polylang + W3 Total Cache 跨 Host 缓存失效问题

WordPress 多域名环境下中文文章详情页未显示 Polylang 语言切换器

(13) WordPress 多域名下 Polylang 语言切换器延迟:W3TC Redis 跨 Host 缓存排查记录

图8:选择 2 核 4 GiB 后显示的实际补差价、2 Mbps 带宽和重启选项

(14) 从 CPU 再次告警到 ECS 升配:WordPress 服务器从 1 核 2G 升级到 2 核 4G 实录

**Alt:** WordPress 生产环境完成 PHP 8.5.9 升级,终端显示 OPcache、Imagick、Redis、Nginx、WordPress 版本及中英文站和后台域名均正常返回 HTTP 200

(15) OneinStack 生产环境将 PHP 8.1.19 升级到 PHP 8.5.9:Imagick 编译失败与 WordPress 多域名缓存验收

阿里云 OneinStack 服务器升级完成,终端显示 Nginx 1.30.4、OpenSSL 3.5.7、PCRE 8.45 和 PHP 8.5.9,Nginx 配置检查成功

(16) 阿里云 OneinStack 实战:将 Nginx 1.24.0 升级到 1.30.4,并同步升级 OpenSSL 3.5.7

阿里云 OneinStack 服务器 Redis 从 7.0.11 升级到 8.10.0 后的版本与运行状态对比截图

(17) 阿里云 OneinStack 实战:将 Redis 7.0.11 升级到 8.10.0,并完成内核优化与回滚保护

GitHub 上为 PublishPress Series 提交 PHP 8.5 SplObjectStorage 弃用警告 Issue 的页面

(18) WordPress 升级 PHP 8.5 后的插件兼容性排查:该修的修,该停的停,该等上游的等

WordPress Post Views Counter 热门文章排行榜区块及浏览量设置界面,用于排查首页查询性能问题

(19) WordPress 动态首页从 19 秒降到 1 秒以内:Post Views Counter 热门文章查询性能问题排查与 MU Plugin 优化实战

WordPress 撰写设置中已开启可能影响网站性能的 Gutenberg 实时协作功能

(20) WordPress 7.0 + PHP 8.5 服务器配置全面审计:PHP-FPM、OPcache、Redis、RDS 与 WordPress 调优实战

【图 1:8 月 6 日提交的 PublishPress Series PHP 8.5 兼容性 Issue #1163 已由上游关闭】

(21) PublishPress Series 3.1.3 升级实录:PHP 8.5 Issue 已修复,却又遇到 Gutenberg 系列编号回归

图 6:为 www 域名单独启用适中的自适应频控和流量防盗刷,处置方式均采用 JavaScript 挑战

(22) WordPress 再次出现 CPU 告警:从 EdgeOne 异常流量到自适应频控与 JavaScript 挑战

图 3:PHP-FPM 日志出现 Allowed memory size of 268435456 bytes exhausted

(23) WordPress CPU 告警继续排查:Yoast Sitemap 500、PHP 256M OOM 与 W3TC 预热失效

图 2:新的 PHP 进程通过 get_option("cron") 读取不到 Probe,但数据库中明确存在

(24) W3TC 定时任务为何创建后又消失?排查 WP-Cron 与 Redis alloptions 陈旧缓存

上一篇继续排查了 W3 Total Cache 页面缓存长期很难看到超过 2 天的问题。

最终先发现了一条已经持续了一段时间的故障链:

Plaintext
Yoast post-sitemap.xml

PHP-FPM 256M 内存耗尽

HTTP 500

联合预热 Sitemap 长期生成失败

W3TC 无法持续获得最新的预热 URL

将 PHP-FPM 的 memory_limit 从实际生效的 256M 提高到 512M 后,中英文 post-sitemap.xml 都恢复了 HTTP 200,联合 Sitemap 也重新开始自动更新。

到这里,我原本以为 W3TC 页面缓存预热的问题基本解决了。

但继续检查以后,又发现了一个更加奇怪的问题:

W3 Total Cache 的 Prime 和 Cleanup 定时任务虽然配置存在,却没有正常执行。手工重新创建以后,任务甚至还会再次消失。

继续往下排查,最终复现出了一组非常关键的结果:

Plaintext
数据库中的 cron option:YES
WordPress get_option("cron"):NO

也就是说:

MySQL 中保存的 Cron 数据已经是新版本,但普通 PHP 环境从 Object Cache 读取到的,却仍然是一份旧数据。

最后通过精准删除 Redis Object Cache 中的 options/alloptions,Cron 数据重新恢复一致。

这一篇就记录整个排查过程。


一、Sitemap 已经恢复,但 W3TC Prime 还是没有正常工作

W3 Total Cache 当前开启了 Page Cache Preload。

配置大致为:

Plaintext
Page Cache:Disk Enhanced

Prime:
每 300 秒执行一次
每次预热 7 个 URL

Cleanup:
每 3600 秒执行一次

对应的两个 WP-Cron Event 是:

Plaintext
w3_pgcache_prime
w3_pgcache_cleanup

联合 Sitemap 恢复以后,我开始确认这两个任务到底有没有正常运行。

结果发现它们的状态明显不对。

重新创建以后,WP-CLI 能够看到:

Plaintext
w3_pgcache_prime
2026-09-01 04:09:28
now
5 minutes

w3_pgcache_cleanup
2026-09-01 04:09:28
now
1 hour
图 1:重新创建的 W3TC Prime 与 Cleanup 已经到达执行时间
图 1:重新创建的 W3TC Prime 与 Cleanup 已经到达执行时间

这里显示的是 GMT 时间。

对应北京时间就是:

Plaintext
12:09:28

Prime 的周期是 5 分钟,Cleanup 的周期是 1 小时。

正常情况下,只要 WP-Cron 真正执行一次,这两个任务下一次执行时间就应该分别向后推进。

但实际情况并不是这样。


二、Linux Cron 明明一直在执行 wp-cron.php

我的 WordPress 已经关闭了由前台访问自动触发 WP-Cron:

PHP
DISABLE_WP_CRON = true

服务器则使用 Linux Cron,每 5 分钟直接执行一次:

Plaintext
*/5 * * * * cd /data/wwwroot/www.shuijingwanwq.com && /usr/local/php/bin/php wp-cron.php >/dev/null 2>&1

因此理论上的链路是:

Plaintext
Linux crond

php wp-cron.php

WordPress WP-Cron

w3_pgcache_prime
w3_pgcache_cleanup

检查 /var/log/cron 后,也能够确认 wp-cron.php 每 5 分钟确实都在执行。

所以问题并不是:

Linux Cron 没启动。

甚至手工执行:

Bash
/usr/local/php/bin/php wp-cron.php

也能够正常返回:

Plaintext
exit_code=0

但当时两个 W3TC Event 的时间仍然没有按照预期推进。

于是问题只能继续往 WordPress Cron 内部追。


三、先逐项排除最常见的 WP-Cron 问题

接下来检查了几个最直接的可能性。

首先确认:

PHP
wp_get_ready_cron_jobs()

能够看到:

Plaintext
w3_pgcache_prime
w3_pgcache_cleanup

也就是说 WordPress 明确认定它们已经到执行时间。

再检查:

PHP
has_action("w3_pgcache_prime")
has_action("w3_pgcache_cleanup")

两者也都已经注册 callback。

同时:

PHP
get_transient("doing_cron")

没有发现长期残留的 Cron lock。

因此逐步排除了:

Plaintext
任务根本不存在                ×
任务还没有到时间              ×
W3TC callback 没有注册        ×
长期 doing_cron 锁卡死        ×
Linux crond 没运行            ×

到这里问题已经有些反常了。


四、更奇怪的是,两个 W3TC Event 后来直接从数据库消失了

继续检查以后,出现了一个比“不执行”更加奇怪的现象。

直接读取 WordPress 数据库里的:

Plaintext
wp_options
option_name = cron

发现:

Plaintext
w3_pgcache_prime:NOT IN DATABASE
w3_pgcache_cleanup:NOT IN DATABASE

也就是说:

刚刚才创建成功的两个 W3TC Cron Event,后来竟然从真正的数据库中消失了。

如果只是 W3TC Prime callback 执行失败,理论上很难解释这种现象。

这时我开始怀疑:

会不会不是某个 Event 被单独删除,而是整个 cron option 曾经被一份旧数据覆盖?

为了验证这个猜测,需要一个完全与 W3TC 无关的实验任务。


五、创建一个 WordPress 插件根本不认识的 Probe

我先创建了一个临时 Cron Event:

Plaintext
swq_cron_persist_probe

并把执行时间安排在大约 1 小时以后。

这个名字是临时生成的,任何现有插件都不可能提前针对它编写删除逻辑。

创建以后检查:

Plaintext
schedule_result=bool(true)

DB_contains_probe=YES
get_option_contains_probe=YES

说明任务确实已经写入数据库。

然后立即执行:

Bash
/usr/local/php/bin/php wp-cron.php

注意:

这个 Probe 还有大约 1 小时才到期,所以此时的 wp-cron.php 根本不应该处理它。

但执行以后再次检查:

Plaintext
DB_contains_probe=NO
get_option_contains_probe=NO
next_scheduled=bool(false)

Probe 居然消失了。

这一下就基本排除了:

W3TC 自己专门清除了 w3_pgcache_prime

因为一个刚刚才临时起名字、还没有到期的 Event 也一样会消失。

真正值得怀疑的变成:

Plaintext
wp-cron.php 启动

读取到旧的 Cron 数组

执行某个到期 Event

重新写回 cron option

后来新增的 Event 被旧数组一起覆盖

不过还需要进一步证明:

WordPress 到底有没有真的读到一份旧 Cron 数据?


六、关键证据出现:DB=YES,但 get_option=NO

于是又建立了第二个 Probe:

Plaintext
swq_cron_persist_probe_2

这次安排在大约 2 小时以后。

创建以后直接查询数据库:

Plaintext
schedule=bool(true)
DB=YES

说明数据库中确实已经存在这个 Event。

然后启动一个全新的普通 PHP 进程,通过 WordPress:

PHP
get_option("cron")

读取。

结果变成:

Plaintext
get_option=NO
DB=YES
图 2:新的 PHP 进程通过 get_option("cron") 读取不到 Probe,但数据库中明确存在
图 2:新的 PHP 进程通过 get_option(“cron”) 读取不到 Probe,但数据库中明确存在

这组结果非常关键。

因为同一个时间点:

Plaintext
MySQL:
Probe = YES

WordPress get_option("cron"):
Probe = NO

已经不能再用“任务没创建成功”来解释。

数据库明明是新的。

但 WordPress API 读出来的却是旧的。

进一步在:

PHP
define("DOING_CRON", true);

的环境中检查,结果仍然一样:

Plaintext
get_option=NO
DB=YES

因此问题也不是简单的:

只有 WP-Cron 环境才看不到任务。

更准确地说,是新的普通 PHP 进程取得的 option 数据就已经和数据库不一致了


七、把注意力转向 W3TC Redis Object Cache

当前 WordPress 开启了 W3 Total Cache Object Cache:

Plaintext
Object Cache:Enabled
Engine:Redis

而 WordPress 的:

Plaintext
cron

又是一个 autoload option。

检查数据库后得到:

Plaintext
autoload=on

这意味着它通常会随着大量自动加载配置一起进入:

Plaintext
alloptions

因此:

PHP
get_option("cron")

不一定每一次都直接查询 MySQL。

在开启持久化 Object Cache 的环境中,它很可能直接读取已经缓存的:

Plaintext
group:options
key:alloptions

到这里,前面的现象就有了一个非常合理的解释:

Plaintext
数据库中的 cron
已经更新

Redis 中的 alloptions
仍然是旧版本

新的 PHP 进程
get_option("cron")

得到旧数据

但猜测还不够,下一步直接验证。


八、精准删除 alloptions,而不是清空全部缓存

这里我没有执行:

Plaintext
W3TC Flush All

也没有清空 Page Cache。

因为整个排查本来就是为了弄清楚页面缓存生命周期异常的问题,如果此时再把全部页面缓存清掉,反而会破坏后续观察。

所以只针对:

Plaintext
options / alloptions

做最小范围处理。

一开始通过 WP-CLI 删除以后,曾出现一个很有意思的情况:

Plaintext
alloptions=bool(true)

但新的普通 PHP 进程仍然:

Plaintext
get_option=NO
DB=YES

这说明当前环境中:

WP-CLI 与普通 PHP 对 Object Cache 的实际观察结果存在差异。

因此后来改成与 wp-cron.php 相同的普通 PHP 环境执行:

PHP
wp_cache_delete("alloptions", "options");

结果:

Plaintext
delete_alloptions=bool(true)

紧接着启动一个新的普通 PHP 进程重新读取。

这一次变成:

Plaintext
get_option=YES
DB=YES
图 3:删除普通 PHP 环境中的 options/alloptions 后,get_option("cron") 立即与数据库重新一致
图 3:删除普通 PHP 环境中的 options/alloptions 后,get_option(“cron”) 立即与数据库重新一致

这一组前后对比已经非常直接:

Plaintext
删除前:

get_option=NO
DB=YES



删除 alloptions



删除后:

get_option=YES
DB=YES

因此,到这里已经可以比较明确地确认:

这次 WP-Cron Event 消失的直接故障机制,与陈旧的 alloptions Object Cache 有关。


九、为什么陈旧 alloptions 会导致未来 Event 一起消失?

WordPress Cron 的所有任务,本质上都保存在:

Plaintext
option_name = cron

这一份结构化数据中。

如果数据库里已经是:

Plaintext
Cron Version B

其中包含:

Plaintext
旧任务
+
新加入的 W3TC Prime
+
新加入的 Cleanup
+
Probe

但某个 PHP 进程从 Redis Object Cache 中取得的仍然是:

Plaintext
Cron Version A

只包含:

Plaintext
旧任务

那么当这个进程执行某个已到期任务并触发:

Plaintext
wp_reschedule_event()

或者:

Plaintext
wp_unschedule_event()

时,就有可能基于自己手里这份旧数组重新写回数据库。

于是:

Plaintext
数据库 Version B

被 Version A 重新覆盖

后来新增的 Event 全部消失

这也解释了之前那个最反常的现象:

为什么一个还有 1 小时、2 小时才执行的 Probe,也会在运行 wp-cron.php 后提前消失。

它并不是被 Cron “执行”了。

而更可能是:

它所在的新版本 Cron 数据整体被旧版本覆盖掉了。


十、重新建立 Prime、Cleanup 和 Probe

清理陈旧的 alloptions 以后,我重新创建:

Plaintext
w3_pgcache_prime
w3_pgcache_cleanup

并保留:

Plaintext
swq_cron_persist_probe_2

用于继续验证。

随后数据库检查得到:

Plaintext
DB w3_pgcache_prime=YES
DB w3_pgcache_cleanup=YES
DB swq_cron_persist_probe_2=YES
图 4:W3TC Prime、Cleanup 与测试 Probe 均重新存在于数据库中
图 4:W3TC Prime、Cleanup 与测试 Probe 均重新存在于数据库中

截图因为终端一屏显示空间有限,没有完整包含前面的创建命令。

不过这里真正需要确认的是最终数据库状态:

Plaintext
Prime   = YES
Cleanup = YES
Probe   = YES

说明三个 Event 此时已经同时存在。

同时检查它们的执行时间:

Plaintext
w3_pgcache_prime
05:39:35 GMT

w3_pgcache_cleanup
05:39:35 GMT

swq_cron_persist_probe_2
07:34:18 GMT

其中 Prime 和 Cleanup 已经到期,Probe 仍然要将近两个小时以后才执行。

正好适合再次测试 wp-cron.php


十一、再次执行 wp-cron.php,这一次终于正常了

执行:

Bash
/usr/local/php/bin/php wp-cron.php

以后再次检查。

Prime 从:

Plaintext
05:39:35 GMT

变成:

Plaintext
05:44:35 GMT

刚好:

Plaintext
+5 分钟

Cleanup 从:

Plaintext
05:39:35 GMT

变成:

Plaintext
06:39:35 GMT

刚好:

Plaintext
+1 小时

而 Probe:

Plaintext
07:34:18 GMT

保持完全不变。

数据库中:

Plaintext
DB w3_pgcache_prime=YES
DB w3_pgcache_cleanup=YES
DB swq_cron_persist_probe_2=YES

也全部继续存在。

图 5:修复后 Prime 正常推进 5 分钟、Cleanup 正常推进 1 小时,未来 Probe 保持不变
图 5:修复后 Prime 正常推进 5 分钟、Cleanup 正常推进 1 小时,未来 Probe 保持不变

这一次的结果与故障状态形成了非常鲜明的对比。

修复前:

Plaintext
执行 wp-cron.php

未来 Probe 消失

修复后:

Plaintext
执行 wp-cron.php

Prime +5 分钟
Cleanup +1 小时
Probe 不变

到这里,手工测试已经形成闭环。


十二、最后还需要证明系统 Cron 自己也能正常工作

手工运行正常还不够。

生产环境实际依赖的是 Linux crond:

Plaintext
*/5 * * * *

所以最后我没有再手工执行 wp-cron.php,而是等待下一次系统 Cron。

13:45:01,/var/log/cron 中明确出现:

Plaintext
Sep 1 13:45:01 ...
/usr/local/php/bin/php wp-cron.php
图 6:Linux crond 在 13:45:01 自动执行生产环境的 wp-cron.php
图 6:Linux crond 在 13:45:01 自动执行生产环境的 wp-cron.php

这张截图因为终端显示空间有限,只保留了系统 Cron 的实际执行记录。

随后继续检查 W3TC Event 状态,Prime 已经从此前的:

Plaintext
05:44:35 GMT

自动推进到:

Plaintext
05:49:35 GMT

而尚未到期的 Cleanup 仍然保持:

Plaintext
06:39:35 GMT

这意味着真正的生产链路也已经恢复:

Plaintext
Linux crond

php wp-cron.php

WordPress Cron

w3_pgcache_prime

下一次执行时间 +5 分钟

所以最终并不是:

只有手工运行才正常。

系统每 5 分钟一次的真实 Cron 也已经能够正常驱动 W3TC Prime。


十三、这次真正可以确定的是什么?

经过这一轮实验,有几个结论已经比较明确。

1. Linux crond 没有坏

日志一直显示:

Plaintext
每 5 分钟执行一次 wp-cron.php

最后修复完成后,同一套 Cron 配置也确实能够让 Prime 自动推进。

所以没有必要修改 Linux Cron。


2. wp-cron.php 本身也没有坏

故障期间:

Plaintext
php wp-cron.php

虽然返回:

Plaintext
exit_code=0

但行为异常。

清理陈旧 alloptions 以后,完全相同的:

Plaintext
php wp-cron.php

马上恢复正常。

所以问题并不在 wp-cron.php 文件本身。


3. W3TC Prime / Cleanup callback 本身没有坏

恢复一致性以后:

Plaintext
Prime   +5 分钟
Cleanup +1 小时

都完全符合 W3TC 当前配置。

因此也没有必要修改 Prime callback。


4. 直接故障机制已经定位到陈旧 alloptions

最关键的实测结果仍然是:

Plaintext
DB=YES
get_option=NO

随后:

Plaintext
delete alloptions

立即变成:

Plaintext
DB=YES
get_option=YES

这是整个排查过程中最重要的一组证据。


十四、但最底层根因还没有完全结束

这里需要区分两个概念:

找到直接故障机制,不等于已经找到了最底层根因。

现在已经知道:

Plaintext
普通 PHP 环境中的 alloptions
曾经存在陈旧数据

但还没有完全解释:

为什么数据库中的 autoload option 已经更新,而这份 Redis Object Cache 却没有同步失效?

我的 WordPress 环境又比较特殊。

目前至少涉及:

Plaintext
www.shuijingwanwq.com
en.shuijingwanwq.com
admin.shuijingwanwq.com

后台主要通过:

Plaintext
admin.shuijingwanwq.com

访问,同时使用 Polylang 管理中英文内容。

此前为了处理 Polylang 与 W3 Total Cache 在多域名环境中的缓存问题,我已经增加过 MU Plugin。

但从这次情况来看:

现有兼容处理可能并没有覆盖完整的多域名缓存一致性问题。

不过目前还不能直接断言:

Plaintext
admin 域名

导致 alloptions 陈旧

因为还需要继续确认:

  • WP-CLI 与普通 PHP 使用的 Redis namespace 是否完全一样;
  • adminwwwen 三个 Host 下的 Object Cache key 是否真的一致;
  • W3TC 是否对 WP-CLI 使用了特殊 Object Cache 处理;
  • 当前 MU Plugin 到底只处理了哪些 Page Cache / Object Cache 场景。

这一部分更适合以后单独调查。


十五、Page Cache 和 Object Cache 也不能混为一谈

这次排查还让我更加明确了一点:

Plaintext
W3TC Page Cache

和:

Plaintext
W3TC Object Cache

虽然都叫“缓存”,但其实是两套完全不同的问题。

Page Cache 当前使用:

Plaintext
Disk Enhanced

主要缓存的是最终 HTML 页面。

而这一次影响 WP-Cron 的:

Plaintext
alloptions

属于:

Plaintext
Object Cache / Redis

所以不能因为 Object Cache 出现了数据一致性问题,就直接认为:

W3TC 的页面文件缓存也一定坏了。

反过来也一样。

接下来观察:

Plaintext
4 天 Page Cache TTL

是否终于能够逐渐出现 2 天、3 天甚至接近 4 天的缓存文件,仍然需要单独进行。


十六、为什么这次没有直接 Flush All?

整个过程中我一直尽量避免:

Plaintext
W3TC Flush All

原因也很简单。

这台服务器只有 2 vCPU,而且这一次问题本来就是因为 CPU 告警才重新开始排查。

如果直接清空全部页面缓存,随后大量页面都需要重新生成,反而可能制造一次新的缓存重建压力。

而且这一次已经能够把问题精确缩小到:

Plaintext
options / alloptions

那么最合理的做法就是:

Plaintext
只处理真正异常的缓存

而不是:

Plaintext
为了省事把所有缓存全部清掉

最终也确实证明,只处理 alloptions 就足以恢复 Cron 数据一致性。


十七、以后服务器终端中的 WordPress 操作尽量统一使用 WP-CLI

这次为了定位问题,我交替使用了:

Plaintext
WP-CLI
普通 PHP CLI
wp-cron.php
数据库直接查询

这是故障诊断所必须的。

特别是如果没有刻意比较:

Plaintext
WP-CLI
vs
普通 PHP

可能根本发现不了:

Plaintext
DB=YES
get_option=NO

这样的差异。

不过日常运维长期这样混用,会增加很多理解成本。

所以后续准备尽量遵循一个原则:

Plaintext
服务器终端中的 WordPress 日常管理
→ 优先 WP-CLI

需要验证 Web / PHP / WP-Cron 真实运行环境
→ 才临时使用普通 PHP

至于目前系统 Cron:

Bash
/usr/local/php/bin/php wp-cron.php

暂时不准备为了“形式统一”改成:

Bash
wp cron event run --due-now

因为现有生产链路已经经过真实验证:

Plaintext
Linux crond
→ php wp-cron.php
→ W3TC Prime

现在运行正常。

在 WP-CLI 与普通 PHP 的 Object Cache 差异还没有完全调查清楚以前,没有必要再额外改变一个已经正常工作的生产入口。


十八、三层问题终于逐步拆开

回头看这次 CPU 告警,最后实际上拆出了三层完全不同的问题。

第一层是流量入口:

Plaintext
短时间大量异常访问

EdgeOne

自适应频控
JavaScript Challenge
流量防盗刷

第二层是页面预热入口:

Plaintext
Yoast post-sitemap.xml

256M OOM

HTTP 500

联合 Sitemap 长期无法更新

第三层则是 WordPress Cron 与 Object Cache:

Plaintext
cron option 已经更新

Redis alloptions 仍然陈旧

get_option("cron") 读到旧值

wp-cron.php 用旧 Cron 数据工作

后来创建的 Event 被覆盖

三个问题叠在一起时,很容易感觉:

W3TC 好像哪里都不正常。

但真正逐层拆开以后,每一层实际上都能够独立验证。

这也是这次排查最有价值的地方。


十九、接下来先观察,不再继续大改配置

目前 W3TC Prime 已经恢复:

Plaintext
每 5 分钟
预热 7 个 URL

Cleanup:

Plaintext
每 1 小时

联合 Sitemap 也已经恢复持续更新。

Page Cache 生命周期仍然保持:

Plaintext
4 天

现在更重要的事情不是继续调整参数,而是让系统稳定运行一段时间。

如果后续缓存目录逐渐开始出现:

Plaintext
1 天
2 天
3 天

的页面缓存,就说明最开始那个:

为什么 4 天 TTL 却长期看不到超过 2 天的页面缓存?

也终于开始得到实际验证。

而多域名、Polylang、admin.shuijingwanwq.com、WP-CLI 与 W3TC Redis Object Cache 之间为什么会产生这次 alloptions 不一致,则可以作为下一轮独立排查的问题。

至少目前,这次从 CPU 告警一路挖出来的三个问题,终于都已经有了比较明确的处理结果。

PublishPress Series 3.1.3 升级实录:PHP 8.5 Issue 已修复,却又遇到 Gutenberg 系列编号回归

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