WordPress 打开慢,其实有三种完全不同的病:网络/主机慢、服务端 PHP 与数据库慢、前端资源慢。三件事混在一起查,很容易在错误的那一层耗掉半天。
下面这套顺序的原则是「从外到内、每层先证伪」:外层没排除,内层的所有数字都不可信。
URL="https://example.com/"
echo "== 未命中页面缓存 =="
for i in 1 2 3 4 5; do
curl -sS -o /dev/null \
-w 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
"${URL}?nocache=${RANDOM}"
done
echo "== 命中页面缓存 =="
for i in 1 2 3 4 5; do
curl -sS -o /dev/null \
-w 'ttfb=%{time_starttransfer} total=%{time_total}\n' \
"$URL"
done输出单位是秒。time_namelookup 是 DNS,time_connect 是 TCP 握手,time_appconnect 是 TLS 握手,time_starttransfer 是首字节(TTFB),time_total 是 HTML 接收完。
粗略判断:
在同一台机器上取一个不经过 PHP 的静态文件做对照:
curl -sS -o /dev/null \
-w 'static ttfb=%{time_starttransfer} total=%{time_total}\n' \
https://example.com/wp-includes/css/dashicons.min.css顺手看一眼服务器状态(在服务器上跑):
uptime
vmstat 1 5
iostat -x 1 3 2>/dev/null | head -20vmstat 里的 wa(I/O 等待)长期偏高,说明磁盘是瓶颈。先换盘或减少写操作,再谈缓存。
先看响应头:
curl -sSI https://example.com/ | grep -iE '^(age|x-cache|cf-cache-status|cache-control|server|vary):'有 age 且大于 0,说明命中了上游缓存。连续请求两次,如果 age 递增、TTFB 明显下降,页面缓存是工作的。
再确认持久化对象缓存是否真的挂上了:
wp eval 'var_dump(wp_using_ext_object_cache());'返回 false,意味着每个请求的 options、用户、term 都还在查数据库,这通常是 TTFB 高的主因。
判断标准很简单:先让未命中缓存的请求变快,缓存才有意义;反过来,如果命中率低,先修命中率,因为这一步收益比改代码大。
常见的命中率杀手:登录态被整页缓存排除、URL 带随机参数被当成不同页面、CDN 回源规则把动态页全部穿透。逐条核对,不要凭感觉。
在 wp-config.php 里临时打开:
define('SAVEQUERIES', true);新建 wp-content/mu-plugins/perf-query-counter.php:
<?php
/**
* Plugin Name: Perf Query Counter
* Description: 把每个请求的 SQL 次数与耗时写进 error_log,仅对登录的管理员生效。
*/
add_action('shutdown', function () {
if (!defined('SAVEQUERIES') || !SAVEQUERIES) {
return;
}
if (!is_user_logged_in() || !current_user_can('manage_options')) {
return;
}
$db = $GLOBALS['wpdb'];
$time = 0.0;
foreach ((array) $db->queries as $q) {
$time += (float) $q[1];
}
error_log(sprintf(
'[perf] uri=%s queries=%d db=%.3fs peak=%s',
$_SERVER['REQUEST_URI'] ?? '-',
count((array) $db->queries),
$time,
size_format(memory_get_peak_usage(true))
));
}, 999);mu-plugins 目录不存在就新建。SAVEQUERIES 本身有开销,测完立刻删掉。
浏览器登录后台,再访问要测的页面,然后看 wp-content/debug.log 或服务器 error_log:
queries 在几十以内、db 小于 0.1s:数据库不是瓶颈,去看 PHP 执行和外部 HTTP 请求。queries 上百、db 超过 0.5s:继续往下查。wp db query "SELECT option_name, LENGTH(option_value) AS bytes
FROM wp_options
WHERE autoload IN ('yes','on')
ORDER BY bytes DESC
LIMIT 15;"表前缀不是 wp_ 就换成自己的。autoload 里的每一行都会在每个请求上被读出来并反序列化,几百 KB 的 autoload 就是几百毫秒的固定开销,通常来自被卸载插件残留的 option。
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_output = 'TABLE';跑几个页面之后再查:
SELECT start_time, query_time, rows_examined, LEFT(sql_text, 200) AS sql_head
FROM mysql.slow_log
ORDER BY query_time DESC
LIMIT 10;rows_examined 远大于返回行数,就是缺索引;sql_text 里出现 LIKE '%...%',或者对 meta 表的无索引查询,是 WordPress 最常见的两个坑。
同时确认不是被锁住了:
wp db query "SELECT id, user, time, state, LEFT(info, 120) AS info
FROM information_schema.processlist
WHERE command <> 'Sleep'
ORDER BY time DESC;"state 里出现大量 Waiting for table metadata lock 或 Locked,说明有别的东西在长时间写表(备份、导入、定时任务)。这时候优化页面没用,先处理掉长事务。
统计首页加载了多少个外部域名的脚本:
curl -sS https://example.com/ > page.html
grep -oE '<script[^>]+src="[^"]+"' page.html \
| sed -E 's/.*src="([^"]+)".*/\1/' \
| awk -F/ '{print $3}' \
| sort | uniq -c | sort -rn自己域名之外的每一行都是一个第三方。TTFB 正常、但页面转圈很久才能点的情况,八成出在这里。
定位到具体插件用二分法,不要猜:
wp plugin list --status=active --field=name先停一半(wp plugin deactivate 插件a 插件b),复测 TTFB 与前端耗时,再对半砍,几轮就能锁定。每次只改一个变量,测完再改下一个。
WooCommerce 站点还要单独看购物车相关的请求:未登录用户在每个页面都触发购物车刷新,会让「看起来是静态页」的页面变成动态请求。
# 从首页 HTML 抽出图片地址,逐个问 content-length
grep -oE 'src="[^"]+\.(jpg|jpeg|png|webp|avif)"' page.html \
| sed -E 's/^src="//; s/"$//' | sort -u > imgs.txt
while read -r u; do
case "$u" in
http*) ;;
*) u="https://example.com${u}" ;;
esac
len=$(curl -sSI "$u" | awk 'tolower($1)=="content-length:"{gsub("\r","",$2); print $2}')
echo "${len:-0} $u"
done < imgs.txt | sort -rn | head -20排在前面的几张如果单张过大,先压它们:换成 WebP/AVIF、按显示尺寸出图(不要拿原图缩显)、首屏之外的图加懒加载。这一层的收益是线性的,改一张少一张。
现象 | 大概率在哪一层 | 先做什么 |
|---|---|---|
静态文件 TTFB 就大于 1s | 主机 / 线路 | 查负载、I/O、带宽,找主机商 |
静态快、首页 TTFB 高,queries 少 | PHP 执行、外部 HTTP | 查插件里的同步外部请求、定时任务 |
首页 TTFB 高,queries 多、db 时间长 | 数据库 | autoload、慢查询、缺索引 |
两次请求 TTFB 差别巨大 | 缓存 | 修命中率与对象缓存 |
TTFB 正常,页面交互卡 | 前端脚本 | 按域名统计、二分停插件 |
首屏图片大、LCP 差 | 资源 | 压缩格式、尺寸、懒加载 |
按这个顺序走一遍,通常一两个小时就能锁定在哪一层。如果你不想自己逐层排查,我们提供定制开发和代部署两种方式(服务说明),也可以先买一份成品模板把站跑起来,再按上面的顺序慢慢调。
需要人替你把这套流程跑完并且落地改动,可以从这里开始:代部署 Setup。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。