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

第三次排查才找到真因:Polylang 标签同步脚本总数长期不一致,原来是持久化 Term Query 缓存

图1:直接 PHP 环境下默认查询返回 8915,禁用缓存后返回 8928

作者:

,

WP 博客多语言化实操

查看按语言划分的用户统计,排名前 4 的语言(英语、中文、印尼语、越南语)(如图3)

(1) 博客多语言化决策|为什么我决定花时间做多语言?(附真实发现)

博客多语言插件选型|免费vs付费、服务端vs浏览器端,实测对比+配置教程

(2) 博客多语言插件选型|免费vs付费、服务端vs浏览器端,实测对比+配置教程

附上我翻译完成后的分类效果截图(如图11),中文与英文的分类总数相等

(3) 博客分类翻译实操|695个分类,4小时手动翻译全流程(避坑指南)

最后分别查看中文与英文后台下的标签统计,符合预期。如图17

(4) 博客标签翻译实操|8060个标签,基于 PHP 脚本实现全流程

如图11:最终效果,在前台,语言切换器的显示

(5) 博客菜单翻译实操

如图20对应场景:网站前台效果,顶部语言切换器(中文/英文),点击英文后,网站整体切换为英文版本,文章列表按发布时间倒序排列(与中文文章排序一致),点击任意英文文章,可正常查看,语言切换流畅;同时,英文文章的发布时间、分类、标签与中文原文完全对应,URL路径规范,SEO友好。

(6) WordPress 多语言博客文章翻译实操全记录(Polylang 插件,附避坑指南)

配图说明(如图10):中文、英文分类管理页面截图(分屏对比),标注两个页面的分类总数,演示数量一致/不一致的场景。

(7) WordPress 新增文章分类标签多语言前置翻译流程(Polylang 总数校验+标签脚本复制避坑)

完成后,访问中文系列页 https://你的域名/series/self-hosted-vpn-series/,点击顶部的 English 切换按钮,就能正常跳转到 https://你的域名/en/series/self-hosted-vpn-series-en/ 了。如图13

(8) 告别手动编号:用 PublishPress Series 优雅管理 WordPress 系列文章

中文标签出现冗余新增(如图2),数据彻底错乱。

(9) WP 6.9 标签同步脚本在 WP 7.0 失效完整排查与解决实录

重点避坑:仅勾选数据库无法完成数据还原,选中库之后,右侧数据表列表点击全选,囊括库内所有数据表(如图3)

(10) 阿里云RDS数据库误操作损毁,完整备份恢复实操避坑指南

为了看起来更完善,AI会主动新增非必要的清理、校验、适配逻辑,简单需求复杂化,大幅增加报错和风险概率(如图7)

(11) AI生成代码深度避坑:数据库操作代码绝不可以直接上线执行

WordPress 标签清理实践(一):大语言模型匹配的失败尝试

(12) WordPress 标签清理实践(一):大语言模型匹配的失败尝试

在宿主机的 output 目录下可以找到生成的 tag_mapping_result.csv。打开文件查看,结果格式规整,符合预期。

(13) WordPress 标签清理实践(二):Go 脚本工程化落地

脚本执行后(如 图 6 终端日志所示),系统开始成对合并中英文标签。

(14) WordPress 标签清理实践(三):完美解决Polylang中英文同义标签合并难题

图7:展示了浏览器开发者工具Network面板中,旧URL 301跳转至新URL的成功记录

(15) WordPress 标签清理实践(四):基于Go脚本实现WordPress中英文标签合并与URL自动跳转

截图 5:验证 301 跳转

(16) WordPress 标签清理实践(五):基于 PHP/Go 脚本解决 English 语言下残留的中文标签问题 ,并实现自动化的标签合并与 URL 跳转

脚本显示处理了 8324 个标签

(17) WP 标签批量翻译脚本准确性问题排查与修复

【图 1,英文首页日历日期链接被拼接成双重 URL】

(18) WordPress 英文站日历链接变成双重 URL:Polylang 子域名迁移后的 WPCode 与 W3TC Object Cache 排查

【图 7,数据库原始内容与 get_post() 返回内容不一致】

(19) WordPress 英文站出现横向滚动条:排查 Polylang 多域名下 W3TC Object Cache 缓存旧 CSS 的问题

【图 1,主题编辑器中的分类列表区块及“以下拉菜单显示”设置】

(20) WordPress Polylang 英文子域名迁移后链接仍指向中文站:分类下拉、面包屑与区块模板的完整修复

【图 1,终端检查 www 与 en 域名统计代码的结果】

(21) WordPress 英文站从 /en/ 迁移到子域名后,如何调整 GA4 与百度统计

WordPress 多域名架构下 W3 Total Cache 缓存失效:Polylang、Redis 与 admin 子域名的完整排查和修复

(22) WordPress 多域名架构下 W3 Total Cache 缓存失效:Polylang、Redis 与 admin 子域名的完整排查和修复

图1:直接 PHP 环境下默认查询返回 8915,禁用缓存后返回 8928

(23) 第三次排查才找到真因:Polylang 标签同步脚本总数长期不一致,原来是持久化 Term Query 缓存

在 WordPress + Polylang 多语言站点中,我一直使用一份 PHP 脚本,将新增的中文标签同步复制为英文标签,并建立中英文翻译关系。

这份脚本已经运行了很长时间,但始终存在一个让人困惑的问题:

WordPress 后台显示的中文标签总数,与脚本实际读取到的中文标签总数偶尔对不上。

这个问题并不是第一次出现。

此前我已经围绕它做过两轮完整排查,并分别整理成了文章:

第一次排查将问题指向 W3 Total Cache 中残留的 Polylang 翻译关系缓存;第二次排查则发现,边遍历边创建标签时使用动态分页,会导致分页范围和统计结果发生变化。(永夜)

这两个问题确实都存在,之前的修复也并非无效。

但是直到这一次,我才确认:

脚本长期出现“后台总数正确、直接运行脚本却少读若干标签”的真正根因,是 get_terms() 命中了持久化的旧 WP_Term_Query 查询结果缓存。

此前两次排查解决的是外围问题和并发问题,却没有彻底处理脚本读取源标签列表时的缓存可靠性。


一、这份脚本是做什么的

我的 WordPress 博客使用 Polylang 管理中英文内容。

在新增中文文章时,通常也会增加一些新的中文标签。为了避免每次都在后台手动创建英文标签,我使用一份命令行 PHP 脚本自动完成以下工作:

  1. 读取所有中文标签;
  2. 判断每个中文标签是否已有英文翻译;
  3. 为缺少翻译的标签创建英文标签;
  4. 设置英文语言归属;
  5. 建立 Polylang 中英文翻译关系;
  6. 输出已处理、已跳过和未完成的标签统计。

脚本的运行方式很简单:

Bash
cd /data/wwwroot/www.shuijingwanwq.com
php polylang-batch-zh-to-en-tags.php

在正常情况下,脚本读取到的中文标签数量,应该与 WordPress 后台显示的中文标签总数一致。

但这次明显不是这样。


二、故障再次出现:后台有 8928 个,脚本却始终只有 8915 个

WordPress 后台显示:

Plaintext
中文标签总数:8928

但无论运行多少次同步脚本,输出始终都是:

Plaintext
源语言标签总数:8915
已处理新标签:0
已跳过已有翻译:8915

两者相差:

Plaintext
8928 - 8915 = 13

更异常的是,这并不是一次性的统计延迟。

我后来继续:

  • 给已发布但没有标签的文章补充标签;
  • 删除部分新标签后重新添加;
  • 发布带有新标签的文章;
  • 再次运行同步脚本。

结果脚本仍然稳定地停留在:

Plaintext
8915

这说明问题已经不是简单的后台统计延迟。

脚本拿到的是一份固定不变的旧标签列表。


三、为什么前两次排查没有真正解决这个问题

回看此前的两轮排查,会发现当时解决的是两个不同层面的问题。

1. 第一次解决的是 Polylang 翻译关系缓存

第一次是在 WordPress 升级后,脚本将几个尚未翻译的标签误判为已经存在英文翻译。

清除 W3 Total Cache 后,脚本重新正常工作。

因此,当时得出的结论是:

W3 Total Cache 中残留了旧的 Polylang 翻译关系缓存,导致 pll_get_term() 返回错误结果。

这个判断在当时的场景下是成立的。清缓存后问题也确实消失了。(永夜)

但当时并没有继续确认:

  • 究竟是哪个缓存组;
  • 是翻译关系缓存还是术语查询缓存;
  • 为什么之后还会复发;
  • 如何从代码层面避免再次命中旧数据。

也就是说,当时解决了症状,却没有建立长期防护。

2. 第二次解决的是动态分页污染

第二次排查时,脚本出现过这样的统计:

Plaintext
源语言标签总数:8328
已处理新标签:0
已跳过已有翻译:9000
处理率:108.07%

当时发现,脚本一边分页读取标签,一边又在数据库中创建新的英文标签。

如果继续使用 offset 或动态分页查询,后续页面的结果集就可能发生移动,导致:

  • 某些标签被跳过;
  • 某些标签被重复读取;
  • 已跳过数量大于实际源标签总数;
  • 最终处理率超过 100%。

最终方案是:

先一次性获取所有中文标签 ID,再通过 array_chunk() 在内存中分批处理。

这一修改解决了动态数据集分页问题。(永夜)

但当时用于“一次性获取所有中文标签 ID”的 get_terms(),仍然允许读取缓存。

也就是说,静态列表虽然不会在运行中变化,但它仍可能从一开始就是一份旧列表。


四、这一次最容易误导判断的地方:WP-CLI 查询竟然是正常的

为了确认是不是 get_terms() 本身的问题,我先通过 WP-CLI 分别测试了默认查询和禁用缓存的查询。

结果是:

Plaintext
默认查询:8928
禁用缓存:8928
差值:0

从这个结果看,很容易得出一个结论:

缓存没有问题,因为两种查询结果完全一致。

我一度也据此认为,问题可能在:

  • 生产文件与仓库文件不一致;
  • 脚本加载顺序不同;
  • Polylang 语言过滤条件异常;
  • PHP CLI 的 OPcache;
  • 某个 MU 插件修改了查询。

但后来按照脚本真正的运行方式,在直接执行 PHP 文件的环境中进行对比,结果完全不同。


五、关键证据:同一个直接 PHP 进程中,默认查询是 8915,禁用缓存是 8928

最终诊断没有继续依赖 WP-CLI,而是完全复现:

Bash
php polylang-batch-zh-to-en-tags.php

这种直接 PHP CLI 执行环境。

在同一执行上下文中分别运行:

PHP
get_terms([
    'taxonomy'   => 'post_tag',
    'lang'       => 'zh',
    'hide_empty' => false,
    'fields'     => 'ids',
]);

以及:

PHP
get_terms([
    'taxonomy'     => 'post_tag',
    'lang'         => 'zh',
    'hide_empty'   => false,
    'fields'       => 'ids',
    'cache_results' => false,
]);

得到的结果是:

Plaintext
默认 get_terms():8915
cache_results=false:8928
差值:13

至此,根因已经非常明确:

直接执行 PHP 脚本时,默认的 get_terms() 命中了持久化的旧 Term Query 查询缓存。

而设置:

PHP
'cache_results' => false,

之后,查询重新从当前数据状态获取完整结果,正确返回了 8928 个中文标签。

图1:直接 PHP 环境下默认查询返回 8915,禁用缓存后返回 8928

六、为什么 WP-CLI 和直接 PHP 脚本会得到不同结果

这也是整个排查中最容易产生矛盾的地方。

WP-CLI 查询得到:

Plaintext
8928

直接 PHP 脚本默认查询却得到:

Plaintext
8915

它们看起来都加载了同一个 WordPress,但运行上下文并不完全相同。

WP-CLI 会定义和初始化自己的运行环境,例如:

PHP
WP_CLI = true

而直接执行普通 PHP 文件时,并不存在相同的 CLI 上下文。

在当前网站中,还同时存在:

  • W3 Total Cache Object Cache;
  • Redis 持久化对象缓存;
  • Polylang 语言过滤;
  • 自定义 MU 插件;
  • 多域名 Host 识别;
  • PHP CLI OPcache。

因此,不同入口可能使用不同的:

  • 缓存命名空间;
  • Host 上下文;
  • 缓存绕过逻辑;
  • 插件初始化路径;
  • 缓存键组合。

这意味着:

WP-CLI 中查询正常,不能证明普通 php file.php 执行环境中的缓存也正常。

真正可靠的诊断,必须复现出现问题的原始执行方式。


七、SQL 本身没有问题,问题发生在查询结果缓存层

诊断过程中还检查了实际生成的 SQL。

查询没有异常的 LIMIT,也没有错误的语言条件:

SQL
SELECT t.term_id
FROM wp_terms AS t
INNER JOIN wp_term_taxonomy AS tt
    ON t.term_id = tt.term_id
INNER JOIN wp_term_relationships AS pll_tr
    ON pll_tr.object_id = t.term_id
WHERE tt.taxonomy IN ('post_tag')
  AND pll_tr.term_taxonomy_id IN (8837)
ORDER BY t.name ASC

也就是说:

  • 数据库中存在完整的中文标签;
  • Polylang 的中文语言关系正确;
  • SQL 查询条件没有漏掉新标签;
  • 数据库实时查询能够返回 8928 个结果。

真正返回 8915 的,是此前保存下来的旧查询结果。

这也解释了为什么数量会长期固定不变。


八、13 个缓存遗漏标签,与 9 个真正缺失翻译标签并不矛盾

诊断过程中还出现了一个看似矛盾的数据:

Plaintext
旧缓存遗漏:13 个中文标签
真正缺少英文翻译:9 个中文标签

这两个数字并不要求相等。

13 个标签没有进入旧的中文源标签列表,但其中有 4 个标签已经通过其他流程建立了英文翻译关系,例如:

  • 后台手动操作;
  • 文章翻译流程自动同步;
  • 其他插件或脚本处理。

因此,当前实际需要补齐英文翻译的只有 9 个。

这 9 个标签分别是:

Plaintext
代码审查
占位符保护
Token 校验
翻译稳定性
安全加速流量
域名拆分
安全加速请求
加量包
网站成本控制

九、最终修复其实只有两行

确认根因后,没有重新设计脚本,也没有大范围重构标签创建流程。

只在获取全部中文标签 ID 的查询中加入一条注释和一个参数:

PHP
// 批处理维护脚本必须绕过持久化的 term-query 缓存,确保读取最新完整标签集合。
$source_term_ids = get_terms([
    'taxonomy'     => 'post_tag',
    'lang'         => $source_lang,
    'hide_empty'   => false,
    'fields'       => 'ids',
    'cache_results' => false,
]);

核心变化只有:

PHP
'cache_results' => false,

对应 Git 提交为:

Plaintext
f079037925dcf048bf1d455cf2a6badfb830df64
fix: bypass stale term query cache in Polylang tag batch

这个修复没有关闭整个 WordPress 的缓存,也没有影响前台页面性能。

它只规定:

这份维护脚本在读取待处理源标签集合时,不可信任旧的查询结果缓存。

对于日常页面请求,缓存可以提高性能。

但对于负责补齐、迁移、校验数据的维护脚本,准确性比复用查询缓存更加重要。


十、生产部署后的第一次运行结果

部署前先对生产文件进行了备份,然后检查临时文件与生产文件语法:

Plaintext
No syntax errors detected in /tmp/polylang-batch-zh-to-en-tags.php.new
No syntax errors detected in /data/wwwroot/www.shuijingwanwq.com/polylang-batch-zh-to-en-tags.php

部署后 SHA256 与仓库文件完全一致:

Plaintext
9b8e1993914571da1bd4f2d6849d6fa318b2572093d86ed5fc686636ce12ac68

随后重新运行脚本:

Bash
php polylang-batch-zh-to-en-tags.php

脚本终于读取到了后台的完整数量:

Plaintext
源语言标签总数:8928

最终统计为:

Plaintext
源语言标签总数:8928
已处理新标签:9
已跳过已有翻译:8919
处理率:100.00%

此前长期固定在 8915 的问题已经消失。

这也从生产运行结果反向证明:

cache_results=false 确实命中了真正的故障点。


十一、剩余 3 个标签未建立关系,是另一个独立问题

第一次完整运行结束后,脚本提示有 3 个标签仍未成功建立英文翻译关系:

Plaintext
Token 校验
占位符保护
翻译稳定性

在新的 PHP 进程中再次调用:

PHP
pll_get_term($source_id, 'en');

三个标签仍然返回:

Plaintext
英文 ID:0

因此,这并不是脚本末尾同一进程缓存造成的误报,而是标签创建或翻译关系绑定阶段的另一个独立问题。

由于只剩 3 个标签,继续改写批处理脚本的成本和风险已经高于手动处理,因此最终直接在 WordPress 后台补充:

Plaintext
Token 校验 → Token Validation
占位符保护 → Placeholder Protection
翻译稳定性 → Translation Stability

对应 slug 为:

Plaintext
token-validation
placeholder-protection
translation-stability

这一小段异常不影响本次关于“源标签总数读取不完整”的根因判断。

相反,它也提醒我:

“进入了处理流程”不等于“最终成功建立了翻译关系”。

今后脚本中的 processed 统计,最好只在最终验证成功后增加,而不是在开始尝试创建标签时就增加。


十二、为什么说这一次才算真正解决

此前两次排查都得到了一个可以让脚本暂时恢复正常的方案:

第一次:

Plaintext
清除 W3 Total Cache

第二次:

Plaintext
一次性获取源标签 ID,再通过 array_chunk() 分批处理

它们分别解决了:

  • Polylang 翻译关系缓存残留;
  • 动态分页过程中结果集变化。

但脚本最关键的源数据入口仍然是:

PHP
get_terms()

只要这个查询继续允许读取持久化旧缓存,就仍然可能再次拿到过期的中文标签 ID 集合。

本次修复则将保证写进了代码:

PHP
'cache_results' => false,

今后无论对象缓存中是否还残留旧的 Term Query 结果,这份批处理脚本都会重新读取当前完整的源标签集合。

这才是一个不依赖人工记忆、不依赖每次先清缓存、也不依赖当前缓存状态的长期修复。


十三、这次排查带来的几个经验

1. 必须在真正出错的执行入口中复现

WP-CLI 正常,不代表:

Bash
php custom-script.php

也正常。

不同运行入口可能拥有不同的缓存与插件上下文。

2. 清缓存可以验证方向,但不一定是永久修复

清除 W3TC 或 Redis 后恢复正常,只能证明问题与缓存有关。

仍需进一步确认:

  • 是哪个查询;
  • 是哪个缓存层;
  • 为什么没有正确失效;
  • 是否可以在关键脚本中主动绕过。

3. 静态列表也可能是一份过期的静态列表

提前获取全部 ID,再用 array_chunk() 处理,确实比动态分页稳定。

但还需要保证这份初始 ID 列表来自实时数据,而不是旧查询缓存。

4. 数据维护脚本应优先保证准确性

前台页面需要缓存性能。

批处理、迁移、修复和校验脚本则应该优先保证:

Plaintext
完整
实时
可验证
可重复运行

对于这种一次运行处理几千个标签的脚本,节省一次术语查询的收益,远低于漏掉标签造成的长期数据不一致。

5. 统计必须代表真正成功,而不是尝试次数

本次输出中的:

Plaintext
已处理新标签:9

实际上包含了 3 个最终未建立翻译关系的标签。

更严谨的统计应该拆分为:

Plaintext
尝试处理数量
成功创建数量
成功绑定翻译关系数量
失败数量
跳过数量

否则,表面上的 100% 处理率仍可能掩盖局部失败。


十四、结语

这个问题前后排查了三次。

第一次,我以为是 WordPress 7.0 升级后残留的 Polylang 缓存。

第二次,我以为是脚本在动态分页过程中创建新标签,导致数据集发生移动。

这两个判断都没有错,但都只是问题的一部分。

真正让脚本长期固定读取旧总数的原因,是:

直接 PHP 执行环境中的 get_terms() 命中了持久化的旧 Term Query 查询结果缓存。

最终修复只有一个参数:

PHP
'cache_results' => false,

但为了确认这一行,前后经历了:

  • 两篇排查文章;
  • 多轮数据库与缓存检查;
  • WP-CLI 与直接 PHP 环境对比;
  • SQL 查询核对;
  • 缓存与非缓存结果差集比较;
  • 仓库提交与生产部署验证。

这也是维护大型 WordPress 多语言站点时很典型的一类问题:

最难排查的并不是直接报错,而是代码正常执行、输出看起来也合理,但读取到的数据从一开始就是旧的。

这一次,关于标签同步脚本“总数长期对不上”的问题,终于不再依赖清缓存碰运气,而是从代码层面得到了稳定解决。

WordPress 多域名架构下 W3 Total Cache 缓存失效:Polylang、Redis 与 admin 子域名的完整排查和修复

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 来减少垃圾评论。了解你的评论数据如何被处理