首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >WordPress 打开慢,按这个顺序查:主机 → 缓存 → 数据库 → 脚本 → 图片

WordPress 打开慢,按这个顺序查:主机 → 缓存 → 数据库 → 脚本 → 图片

原创
作者头像
用户10345225
发布于 2026-10-23 15:42:37
发布于 2026-10-23 15:42:37
360
举报

WordPress 打开慢,其实有三种完全不同的病:网络/主机慢、服务端 PHP 与数据库慢、前端资源慢。三件事混在一起查,很容易在错误的那一层耗掉半天。

下面这套顺序的原则是「从外到内、每层先证伪」:外层没排除,内层的所有数字都不可信。

0. 先把测量条件固定住

  • 用 curl 而不是浏览器:浏览器有缓存、有扩展、有连接预热。
  • 同一个 URL、同一台机器、同一个时段,至少跑 5 次,看中位数而不是单次。
  • 分两组测:带随机参数(绕过页面缓存)和不带参数(命中缓存)。两组差多少,就是缓存带来的收益。
代码语言:bash
复制
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 接收完。

粗略判断:

  • DNS 大于 0.1s:先看解析服务商和本地缓存,这时候还轮不到 WordPress。
  • TCP/TLS 握手大于 0.3s:跨地域或线路问题,先解决接入层。
  • TTFB 大于 1s,且 total 和 TTFB 接近:问题在服务端,继续第 2、3 步。
  • TTFB 正常(小于 0.5s),但浏览器里还是慢:问题在前端资源,跳到第 4、5 步。

1. 主机层:先证明「不是机器的锅」

在同一台机器上取一个不经过 PHP 的静态文件做对照:

代码语言:bash
复制
curl -sS -o /dev/null \
  -w 'static ttfb=%{time_starttransfer} total=%{time_total}\n' \
  https://example.com/wp-includes/css/dashicons.min.css
  • 静态文件也慢 → 主机、带宽、线路、磁盘 I/O 的问题。此时去改主题代码是浪费。
  • 静态文件很快、首页 TTFB 很高 → 这个差值就是 PHP 和数据库的开销,进入下一步。

顺手看一眼服务器状态(在服务器上跑):

代码语言:bash
复制
uptime
vmstat 1 5
iostat -x 1 3 2>/dev/null | head -20

vmstat 里的 wa(I/O 等待)长期偏高,说明磁盘是瓶颈。先换盘或减少写操作,再谈缓存。

2. 缓存层:没命中缓存时做的优化都是幻觉

先看响应头:

代码语言:bash
复制
curl -sSI https://example.com/ | grep -iE '^(age|x-cache|cf-cache-status|cache-control|server|vary):'

有 age 且大于 0,说明命中了上游缓存。连续请求两次,如果 age 递增、TTFB 明显下降,页面缓存是工作的。

再确认持久化对象缓存是否真的挂上了:

代码语言:bash
复制
wp eval 'var_dump(wp_using_ext_object_cache());'

返回 false,意味着每个请求的 options、用户、term 都还在查数据库,这通常是 TTFB 高的主因。

判断标准很简单:先让未命中缓存的请求变快,缓存才有意义;反过来,如果命中率低,先修命中率,因为这一步收益比改代码大。

常见的命中率杀手:登录态被整页缓存排除、URL 带随机参数被当成不同页面、CDN 回源规则把动态页全部穿透。逐条核对,不要凭感觉。

3. PHP 与数据库:把「服务端慢」拆成两个数

3.1 数每次请求打了多少 SQL

在 wp-config.php 里临时打开:

代码语言:php
复制
define('SAVEQUERIES', true);

新建 wp-content/mu-plugins/perf-query-counter.php:

代码语言: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:继续往下查。

3.2 查 autoload 体积

代码语言:bash
复制
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。

3.3 抓慢查询

代码语言:sql
复制
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_output = 'TABLE';

跑几个页面之后再查:

代码语言:sql
复制
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 最常见的两个坑。

同时确认不是被锁住了:

代码语言:bash
复制
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,说明有别的东西在长时间写表(备份、导入、定时任务)。这时候优化页面没用,先处理掉长事务。

4. 脚本:第三方脚本比 PHP 更容易拖垮体验

统计首页加载了多少个外部域名的脚本:

代码语言:bash
复制
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 正常、但页面转圈很久才能点的情况,八成出在这里。

定位到具体插件用二分法,不要猜:

代码语言:bash
复制
wp plugin list --status=active --field=name

先停一半(wp plugin deactivate 插件a 插件b),复测 TTFB 与前端耗时,再对半砍,几轮就能锁定。每次只改一个变量,测完再改下一个。

WooCommerce 站点还要单独看购物车相关的请求:未登录用户在每个页面都触发购物车刷新,会让「看起来是静态页」的页面变成动态请求。

5. 图片与静态资源:最后再看,但常常见效快

代码语言:bash
复制
# 从首页 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 删除。

目录
  • 0. 先把测量条件固定住
  • 1. 主机层:先证明「不是机器的锅」
  • 2. 缓存层:没命中缓存时做的优化都是幻觉
  • 3. PHP 与数据库:把「服务端慢」拆成两个数
    • 3.1 数每次请求打了多少 SQL
    • 3.2 查 autoload 体积
    • 3.3 抓慢查询
  • 4. 脚本:第三方脚本比 PHP 更容易拖垮体验
  • 5. 图片与静态资源:最后再看,但常常见效快
  • 一张判定表
  • 几个常见的误判
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档