本文约 4,000 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:知道站点慢,但不知道是谁慢
站点慢的时候,最常见的困境不是「没工具」,而是「数据太多,看不出结论」。你可能已经在后台装了 Query Monitor,打开面板看到一堆数字:页面生成时间 1.2 秒、数据库查询 180 条、内存占用 64MB。然后呢?这些数字不能直接告诉你该删哪个插件。
Query Monitor 的价值在于它提供了按来源归因的能力。它的查询面板不是简单列一堆 SQL,而是把每条查询归因到「核心 / 主题 / 具体某个插件」,HTTP 面板把每个外部请求归因到发起它的代码位置,钩子面板按耗时排序。用对这几个面板,就能把「站点慢」这个模糊判断,变成「某某插件在这个页面上贡献了 430ms」这样的具体结论。
本文讲怎么用它做一次标准的逐插件性能归因,包括测量前的环境准备、正确的测量流程、关键面板的读法,以及几个能减少误判的注意事项。
原理分析:Query Monitor 采集的是什么数据
Query Monitor 的工作方式是在 WordPress 启动早期挂上收集器,拦截数据库查询、HTTP 请求、钩子调用、脚本与样式注册等事件,然后在页面输出时把结果渲染到管理栏和面板里。它自己也会产生开销,这点后面要专门处理。
几个关键面板对应的归因维度:
| 面板 | 采集内容 | 用来回答什么问题 |
|---|---|---|
| Overview | 页面生成时间、峰值内存、数据库查询时间与条数、条件标签 | 这一页总体有多重 |
| Queries | 全部 SQL,可按 Component 过滤 | 哪个插件在刷数据库 |
| Slow Queries | 超过阈值的查询,默认 0.05 秒 | 哪条 SQL 最该优化 |
| Hooks & Actions | 所有钩子及其回调,按耗时排序 | 哪个钩子上挂的函数最耗时 |
| HTTP API Calls | 所有 wp_remote_* 请求及其耗时、调用栈 |
谁在发外部请求、等了多久 |
| Scripts & Styles | 前端注册的 JS/CSS 及依赖关系 | 前端有多少请求被插件加进来 |
| Environment | PHP 版本、内存限制、OPcache、错误级别 | 环境层面有没有短板 |
| Admin Screen / Template | 当前加载的模板文件、后台页面钩子 | 当前页到底在执行什么 |
其中 Queries 面板的 Component 下拉是本文的核心工具。Query Monitor 会解析每条 SQL 发起时正在执行的 PHP 文件,反推它属于核心(wp-includes)、主题、还是某个具体插件,然后归类。选中某个插件后,面板只显示它发起的查询,并显示条数与耗时合计。
另外两个与配置相关的常量也值得知道:QM_DISABLED 可以全局关闭 Query Monitor;QM_DB_EXPENSIVE 用来调整「慢查询」的判定阈值(默认 0.05 秒),把它调低可以看到更多细节。这两个常量要写在 wp-config.php 里:
// wp-config.php
define( 'WP_DEBUG', true );
define( 'QM_DISABLED', false );
define( 'QM_DB_EXPENSIVE', 0.02 ); // 超过 20ms 的查询标记为慢查询
保姆级实操
第一步:在预发布环境测量,或限制生产环境的影响
Query Monitor 本身会拖慢页面(它会记录每一条查询)。生产站上建议只在排查时段启用,排查完就停用。如果必须常开,可以通过能力判断只给管理员显示。
第二步:固定测量样本
性能测量最怕样本不一致。要做三件事:清掉缓存(页面缓存、对象缓存都要清)、关闭其他插件的调试输出、以及连续测同一页面 5 次取中位数。
# 清对象缓存与 transient,避免命中缓存导致数据失真
wp cache flush
wp transient delete --all
# 连续测 5 次,取 TTFB 中位数
for i in $(seq 1 5); do
curl -s -o /dev/null -w "%{time_starttransfer}\n" -H "Cookie: wordpress_logged_in_xxx=1" https://example.com/
done
第三步:看 Overview 面板拿基线
以管理员身份访问目标页面,看管理栏上的 Overview 数字,记录三个值:Page generation time、Peak memory usage、Database query time。这是你的基线。
第四步:用 Queries by Component 逐个归因
打开 Queries 面板,找到 Component 下拉框,逐个选择插件,记录每个插件的查询条数与总耗时。通常会出现明显的头部效应——一两个插件贡献了绝大部分查询。
重点关注两类异常:条数异常多(比如单个插件 80 条查询,通常是循环里查数据库,即 N+1 问题);单条耗时异常高(通常是缺少索引的全表扫描,或者 meta_query 的复杂 LIKE 匹配)。
第五步:看 HTTP API Calls 面板
这是最容易被忽略但影响最大的一栏。展开每个请求,看它的耗时和调用栈。如果看到某个插件在每个页面加载时都发起授权校验或统计上报请求,且耗时 200ms 以上,那它就是 TTFB 的主要贡献者。解决办法通常是找到该插件的后台开关关闭它,或者给它的选项加缓存:
// 把插件的外部请求结果缓存起来,避免每次页面加载都发起请求
add_filter( 'pre_http_request', 'mmf_cache_plugin_http', 10, 3 );
function mmf_cache_plugin_http( $preempt, $args, $url ) {
if ( false === strpos( $url, 'api.example-vendor.com' ) ) {
return $preempt;
}
$key = 'mmf_http_' . md5( $url );
$cache = get_transient( $key );
if ( false !== $cache ) {
return $cache; // 直接返回缓存,不发起真实请求
}
$response = wp_remote_get( $url, $args );
set_transient( $key, $response, HOUR_IN_SECONDS );
return $response;
}
pre_http_request 是 WordPress 核心过滤器,返回非 false 的值即可短路真实请求。
第六步:看 Hooks & Actions 面板确认时序
按耗时排序,找出耗时最长的几个回调。注意区分:有些必须在 init 上执行的功能无法推迟,而一些「后台才需要」的逻辑被错误地挂在了前台钩子上,这类是纯浪费。
第七步:做 A/B 验证
得出怀疑对象后,停用该插件,重跑第二步的测量,比对差值。差值才是结论,面板上的绝对数字只是线索。这个停用验证的过程如果在生产站做,应该用 Health Check 排障模式 那种只对你当前会话生效的方式,避免影响访客。
常见踩坑
-
把 Query Monitor 自己的开销算进结果:它本身会增加几十毫秒和若干查询。所以结论一定要用「停用前后的差值」来下,而不是看面板绝对值。
-
在缓存命中的页面上测:命中页面缓存时根本没有执行 PHP,测出来的是 CDN 的速度。一定要先清缓存或带登录 Cookie。
-
只看首页:不同页面的插件参与度差异极大。要分别测首页、文章页、归档页、后台文章列表页。
-
忽略慢查询阈值设置:默认 0.05 秒会漏掉很多中等耗时但高频的查询。需要时用
QM_DB_EXPENSIVE调低。 -
一次改多个变量:同时停用三个插件,就不知道是谁的功劳。一次只动一个。
-
案例: 某站点首页 TTFB 稳定在 1.4 秒,站长一直以为是服务器配置问题,升级了两次主机套餐都没改善。用 Query Monitor 的 HTTP API Calls 面板排查后发现,一个社交媒体分享插件在每个页面加载时都向三个境外平台发起计数请求,合计耗时 780ms,而该插件的计数功能在后台已经关闭了——关闭的只是前台显示,请求照发。用上面的
pre_http_request缓存方案处理后,TTFB 降到 430ms,主机套餐也降回了原配置。教训是:先归因,再花钱。 -
不看调用栈:HTTP 面板里显示的调用位置才是定位关键,只看到「有个请求」没用。
总结
标准流程是五步:清缓存固定样本 → Overview 拿基线 → Queries by Component 归因 → HTTP API Calls 查外部请求 → 停用做 A/B 差值验证。
判断标准给三条具体阈值:单个插件在前台单页的查询数超过 30 条、或贡献的查询耗时超过 100ms、或发起超过 1 个外部 HTTP 请求,都值得单独处理。
最后提醒一句:Query Monitor 是排查工具不是常驻插件,生产站排查完就停用。如果排查结果显示问题集中在某个插件上且无法优化,那就进入替换流程,替换前记得按 挑选插件的 8 条硬标准 过一遍候选插件。
