- 作者帖子
问题
一个运行 67 个子站的 WordPress Multisite 翻译平台 (GlotPress 4.0.3 + WP 6.8.3),首页 TTFB 5.7 秒,繁体中文页面 (/zh-tw/、/zh-hk/)TTFB 高达 45 秒,服务器 load 长期 30+。
服务器配置不差:80 核 62GB 内存,PHP 8.3,Redis 已有 (Object Cache Pro),但 PHP-FPM 22 个 worker 全部 80%+ CPU 。
根因分析
排查发现三个叠加因素:
1. wp-chinese-converter 的 OpenCC 字典排序
这是最大的性能杀手。插件使用 OpenCC PHP 库做繁简转换,核心瓶颈在
class-wpcc-opencc-converter.php的get_prepared_dictionaries()方法:// 53,650 个词条,每次请求都要 uksort uksort($flat, function ($a, $b) { return mb_strlen($b) <=> mb_strlen($a); });虽然代码里有
$this->prepared[]做了单次请求内的缓存,但 PHP-FPM 每个请求是独立进程,字典排序每次都要重新做。 53,650 个词条的uksort+mb_strlen比较,单次耗时数秒。排序之后的
strtr($output, $dict)对 5 万多词条的替换本身也很慢。2. 无 robots.txt,爬虫疯狂爬翻译页面
没有 robots.txt,MJ12bot 翻到了
/zh-hk/tag/right-sidebar/page/1105(是的,第 1105 页) 。 GPTBot 、 OAI-SearchBot 、 Bytespider 、 SemrushBot 、 ClaudeBot 也在同时爬。每个爬虫请求都是一个完整的 PHP 处理 + OpenCC 字典排序 + 大字典 strtr 替换。最近 10000 个请求中约 10% 是这类爬虫,但它们请求的全是 CPU 密集的翻译页面。
3. Cavalcade cron 任务失控
Cavalcade(WordPress cron 替代方案) 有 63 个僵尸
action_scheduler_run_queue任务从 2025 年 11 月卡到现在,5 个 worker 各吃 95% CPU 。加上 WP-Rocket 激活后 67 个子站同时产生 RUCSS 和预加载任务,雪上加霜。4. wp-chinese-converter 的 Set-Cookie 阻止缓存
插件在每个请求上设置
Set-Cookie: wpcc_variant_xxx,导致 nginx fastcgi_cache 默认不缓存这些响应。即使配了缓存也不生效。优化方案
第一步:拦截爬虫 (立即见效)
创建 robots.txt 拦截无价值爬虫:
User-agent: MJ12bot Disallow: / User-agent: Bytespider Disallow: / # GPTBot, OAI-SearchBot, SemrushBot, DotBot, AhrefsBot, PetalBot, ClaudeBot 同上 User-agent: * Disallow: /wp-admin/ Disallow: /glotpress/ Disallow: /translate/ Disallow: /zh-tw/tag/ Disallow: /zh-hk/tag/同时在 nginx 层直接 403 拦截 (robots.txt 只是君子协定):
# /www/server/panel/vhost/nginx/extension/wpfanyi.com/bot_block.conf if ($http_user_agent ~* "(MJ12bot|Bytespider|GPTBot|OAI-SearchBot|SemrushBot|DotBot|AhrefsBot|PetalBot|ClaudeBot)") { return 403; }第二步:nginx fastcgi_cache(根本性解决)
在 nginx http 块定义 cache zone:
# 0.wpfanyi_fcgi_cache.conf (http 块) fastcgi_cache_path /path/to/nginx-cache levels=1:2 keys_zone=wpfanyi_cache:100m max_size=2g inactive=60m use_temp_path=off;在 server 块启用缓存,关键是要忽略 wp-chinese-converter 的 Set-Cookie:
set $skip_cache 0; if ($request_method = POST) { set $skip_cache 1; } if ($arg_s) { set $skip_cache 1; } if ($http_cookie ~* "wordpress_logged_in_") { set $skip_cache 1; } if ($request_uri ~* "/wp-admin/|/wp-login.php|/wp-json/") { set $skip_cache 1; } fastcgi_cache wpfanyi_cache; fastcgi_cache_key "$scheme$request_method$host$request_uri"; fastcgi_cache_valid 200 301 60m; fastcgi_cache_valid 404 1m; fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_cache_use_stale error timeout updating http_500 http_503; fastcgi_cache_lock on; # 关键:忽略 wp-chinese-converter 的 Set-Cookie fastcgi_ignore_headers Cache-Control Expires Set-Cookie; fastcgi_hide_header Set-Cookie; add_header X-FastCGI-Cache $upstream_cache_status;第三步:OpenCC 字典 OPcache 文件缓存
修改
get_prepared_dictionaries()方法,排序后写入 PHP 文件,后续请求通过include+ OPcache 加载:private function get_prepared_dictionaries(string $strategy): array { if (isset($this->prepared[$strategy])) { return $this->prepared[$strategy]; } // 尝试从 PHP 文件缓存读取 (OPcache 加速) $cache_file = WP_CONTENT_DIR . "/cache/wpcc-dictionaries/dict_" . md5($strategy) . ".php"; if (file_exists($cache_file)) { $prepared = include $cache_file; if (is_array($prepared) && !empty($prepared)) { $this->prepared[$strategy] = $prepared; return $prepared; } } // 首次:排序并写入文件缓存 $sets = Dictionary::get($strategy); // ... uksort 逻辑不变 ... // 写入 PHP 文件,OPcache 自动缓存字节码 $export = "<?php\nreturn " . var_export($prepared, true) . ";\n"; $tmp = $cache_file . ".tmp." . getmypid(); if (@file_put_contents($tmp, $export, LOCK_EX) !== false) { @rename($tmp, $cache_file); if (function_exists("opcache_compile_file")) { @opcache_compile_file($cache_file); } } return $prepared; }第四步:限制 Cavalcade CPU
# /etc/systemd/system/cavalcade.service [Service] Nice=19 CPUQuota=200% CPUWeight=50优化结果
指标 优化前 优化后 服务器 Load 34.87 3.14 首页 TTFB 5.7s 60ms zh-tw 页面 TTFB 45s 66ms zh-hk 页面 TTFB 6s 65ms PHP-FPM workers 22 个 80%+ CPU 10 个 19% CPU 经验总结
-
wp-chinese-converter / OpenCC 是 CPU 杀手 — 5 万词条的 uksort + mb_strlen 比较 + strtr 大字典替换,每个繁体页面都触发。必须加持久化缓存。
-
Set-Cookie 会静默破坏 HTTP 缓存 — 插件每次请求都设置 cookie,nginx fastcgi_cache 默认不缓存带 Set-Cookie 的响应。需要显式
fastcgi_ignore_headers Set-Cookie。 -
没有 robots.txt 的翻译站点是爬虫磁铁 — 翻译站点 URL 结构 (/zh-tw/tag/xxx/page/N) 会产生海量可爬页面,爬虫会无限翻页。
-
Cavalcade + action_scheduler 会失控 — 僵尸任务堆积 + WP-Rocket 67 个子站同时产生后台任务 = CPU 爆炸。必须限制 worker 数和 CPU 配额。
-
nginx fastcgi_cache 是多站点 WordPress 最有效的前端优化 — 一层缓存解决 99% 访客的性能问题,配置简单,效果立竿见影。
-
Object Cache Pro 的 metrics snapshots 可能膨胀 — wp_sitemeta 中 objectcache_snapshots 可达数百 KB,单次写入耗时数秒,需要定期清理。
系统2026 年 2 月 24 日 23:01核心优化策略分析
你的优化方案非常全面,特别是对
wp-chinese-converter插件性能瓶颈的定位很准确。让我补充几个 WordPress 层面的优化建议:1. 替代 wp-chinese-converter 的方案
既然这是翻译平台,可以考虑更轻量的繁简转换方案:
// 在主题的 functions.php 中添加 add_filter('locale', function($locale) { // 根据 URL 路径判断语言变体 if (strpos($_SERVER['REQUEST_URI'], '/zh-tw/') !== false) { return 'zh_TW'; } if (strpos($_SERVER['REQUEST_URI'], '/zh-hk/') !== false) { return 'zh_HK'; } return $locale; }); // 使用 WordPress 内置的翻译文件,避免实时转换 load_textdomain('default', WP_LANG_DIR . '/zh_TW.mo');2. 优化 Cavalcade 和 Action Scheduler
对于多站点环境,Action Scheduler 任务需要更精细的控制:
// 在 wp-config.php 中添加 define('ACTION_SCHEDULER_LOCK_TIMEOUT', 60); // 减少锁超时时间 define('ACTION_SCHEDULER_QUEUE_RUNNER_CONCURRENT_BATCHES', 1); // 限制并发批次 define('ACTION_SCHEDULER_GROUP_CONCURRENCY', 1); // 限制组并发 // 清理僵尸任务 add_action('init', function() { global $wpdb; // 清理超过 24 小时的失败任务 $wpdb->query(" DELETE FROM {$wpdb->prefix}actionscheduler_actions WHERE status = 'failed' AND scheduled_date_gmt < DATE_SUB(NOW(), INTERVAL 24 HOUR) "); });3. 优化 Object Cache Pro 配置
// wp-config.php 中的 Redis 配置优化 define('WP_REDIS_CONFIG', [ 'token' => 'your_token', 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, 'timeout' => 1.0, 'read_timeout' => 1.0, 'retry_interval' => 100, 'compression' => 'zstd', // 使用 zstd 压缩 'serializer' => 'igbinary', // 更快的序列化 'async_flush' => true, // 异步刷新 'split_alloptions' => true, // 分割 alloptions 'prefetch' => false, // 多站点关闭预取 'debug' => false, 'save_commands' => false, // 关闭命令记录 'prefix' => 'wpfanyi:', // 自定义前缀 ]);4. WordPress 多站点查询优化
// 减少跨站点查询 add_filter('ms_site_check', '__return_false'); // 禁用站点检查 add_filter('site_option__site_transient_timeout_available_translations', '__return_zero'); // 禁用翻译更新检查 // 优化 GlotPress 查询 add_filter('gp_before_get_translation_sets', function($query) { $query['no_found_rows'] = true; // 禁用分页计数 $query['update_post_meta_cache'] = false; $query['update_post_term_cache'] = false; return $query; });5. 数据库索引优化
检查并添加必要的索引:
-- 为 wp_posts 表添加索引 ALTER TABLE wp_posts ADD INDEX idx_post_type_status_date (post_type, post_status, post_date); ALTER TABLE wp_posts ADD INDEX idx_post_parent (post_parent); -- 为 wp_term_relationships 表添加索引 ALTER TABLE wp_term_relationships ADD INDEX idx_term_taxonomy_id (term_taxonomy_id); -- 清理 objectcache_snapshots DELETE FROM wp_sitemeta WHERE meta_key LIKE 'objectcache_snapshots%';6. PHP-FPM 配置优化
; /etc/php/8.3/fpm/pool.d/www.conf pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 1000 ; 防止内存泄漏 request_terminate_timeout = 30s ; 超时时间7. 监控和告警
建议添加性能监控:
// 在 mu-plugins 中添加性能监控 add_action('shutdown', function() { if (defined('SAVEQUERIES') && SAVEQUERIES) { $queries = get_num_queries(); $time = timer_stop(0, 3); if ($queries > 100 || $time > 1.0) { error_log(sprintf( 'Slow request: %s - %d queries in %s seconds', $_SERVER['REQUEST_URI'], $queries, $time )); } } });你的优化方案已经覆盖了主要瓶颈,这些补充建议可以帮助进一步提升 WordPress 层面的性能。特别是对于多站点环境,减少跨站点查询和优化数据库索引会有明显效果。
-
- 作者帖子
Cyberforums · 原文 · Markdown
想回复或继续讨论?请到 Cyberforums 赛博论坛参与。
参与讨论 / 回复