首页 guides MySQL 慢查询治理实战:从日志定位到索引验证

MySQL 慢查询治理实战:从日志定位到索引验证

TL;DR:这篇讨论解决一个很常见的问题:网站偶发卡顿时,如何判断是 MySQL 查询本身慢,还是 VPS(虚拟专用服务器)资源、磁盘 I/O、网络带宽(数据传输能力)拖后腿。我的建议是先开慢查询日志,再用 EXPLAIN 看执行计划,最后用线上相近数据量验证索引,不要一上来就盲目加配置。下面用一个论坛里常见的 WordPress + WooCommerce 场景,把排查链路拆开说清楚。

先说结论:MySQL 慢查询治理不是“加索引”四个字。真正稳定的流程应该是“采样日志 → 归类 SQL → 分析执行计划 → 小范围验证 → 观察业务指标”。如果只看一次后台加载慢,就直接升级云服务器(弹性计算主机)或重启数据库,很容易把问题暂时压下去,却留下更大的索引膨胀、锁等待和缓存抖动。

一、先把问题定性:慢在查询,还是慢在主机资源

我在论坛里见过不少类似帖子:站长说“网站突然慢了”,截图里 CPU 跑到 80%,于是大家开始讨论要不要换更高配置。但从实操看,CPU 高只是现象,不是根因。MySQL 慢查询通常会同时拉高 CPU、磁盘 I/O 和 PHP-FPM 等待时间,单看系统负载很难判断。

建议先做 10 到 30 分钟的轻量采样,时间窗口最好覆盖问题高发时段,比如活动页上线后的前 20 分钟,或者晚高峰 20:00-22:00。这个阶段不要急着改参数,先保留现场。

  • tophtop 看 mysqld 是否长期占用单核 80% 以上。
  • iostat -x 1 10 看磁盘 %util 是否接近 90%,以及 await 是否持续高于 20ms。
  • 用 Web 访问日志统计 5xx、接口耗时和后台请求峰值,确认是否集中在某几个 URL。
  • 在 MySQL 中执行 SHOW PROCESSLIST;,观察是否有同类 SQL 长时间处于 Sending data 或 Copying to tmp table。

如果你同时排查 SSL(安全传输协议)握手和数据库响应,要先把两类耗时分开记录。如果你正在评估主机环境,WHT 上这类 cPanel 主机运维讨论VPS(虚拟专用服务器)速度优化案例 值得一起看。它们能帮助你把“数据库慢”和“主机资源不足”分开,而不是把所有卡顿都归因到服务商或线路。

二、开启慢查询日志:先抓到真实 SQL

进入数据库层后,第一步是让 MySQL 自己说话。慢查询日志能记录超过阈值的 SQL、扫描行数和耗时。测试环境可以把阈值设得低一些,例如 0.5 秒;生产环境建议先从 1 到 2 秒开始,避免日志量突然暴涨。对中小站来说,连续采样 15 分钟通常就能抓到主要问题。

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';
SHOW VARIABLES LIKE 'slow_query_log_file';

如果没有数据库管理员权限,也可以从面板日志、应用调试日志或插件层面采样。WordPress 场景常见的慢 SQL 多集中在 wp_postmetawp_options、订单表和搜索相关查询。日志拿到后,不要逐条手工看,先用摘要工具归类。比如 Percona Toolkit 的 pt-query-digest 能把相似 SQL 合并,按总耗时、平均耗时和扫描行数排序。

pt-query-digest /var/lib/mysql/slow.log --limit 10

这里有个容易踩的坑:不要只看单次最慢的 SQL。一次 9 秒的后台导出未必是线上卡顿主因;反而是 0.8 秒但 300 次重复出现的商品查询,更可能拖垮 PHP 队列。我的习惯是同时看总耗时占比和调用次数,把前 5 类 SQL 拿出来分析。

慢查询日志聚合与问题 SQL 归类

三、用 EXPLAIN 判断索引是否真的命中

拿到问题 SQL 后,下一步才是 EXPLAIN。这里重点看四列:typepossible_keyskeyrows。如果 type 是 ALL,rows 又接近表总行数,基本可以判断发生了全表扫描。如果 possible_keys 有候选索引但 key 为空,通常说明查询条件、排序字段或字段类型让优化器放弃了索引。

EXPLAIN SELECT post_id, meta_value
FROM wp_postmeta
WHERE meta_key = '_price'
  AND meta_value > 100
ORDER BY meta_value DESC
LIMIT 20;

类似 wp_postmeta 这种 EAV 表结构,慢查询特别常见。原因不是 MySQL “不行”,而是数据模型天然依赖大量键值查询;当商品数量从 500 增长到 50000,原本 100ms 的查询可能变成 2 秒以上。这个时候只给 meta_key 单列索引未必够,因为过滤、排序和回表路径都要一起看。

我通常会把执行计划分成三档处理。第一档是明显缺索引,比如 WHERE user_id = ? AND status = ? 没有联合索引;第二档是索引顺序不合适,比如先按低选择性的 status 再按 user_id;第三档是查询写法问题,例如函数包裹字段、隐式类型转换、前置通配符 LIKE。第三档不能靠加索引硬解,否则索引越加越多,写入性能反而下降。

四、索引验证:别在生产库上“凭感觉”上线

索引方案出来后,最重要的是验证。很多事故不是因为没人会写索引,而是因为索引在 1 万行测试库上有效,到了 500 万行生产库就出现锁表、复制延迟或磁盘占用暴涨。中小站也一样,尤其是订单表、评论表和日志表,数据增长速度经常被低估。

一个相对稳妥的做法,是复制一份接近生产规模的数据到测试库,先记录变更前后的执行计划和耗时。下面这个示例不是通用答案,只是说明验证思路:联合索引要跟过滤条件和排序方式匹配,不能看到两个字段就机械地建两个单列索引。

CREATE INDEX idx_order_status_created
ON wp_wc_orders (status, date_created_gmt);

EXPLAIN SELECT id
FROM wp_wc_orders
WHERE status = 'wc-processing'
ORDER BY date_created_gmt DESC
LIMIT 50;

上线前还要估算写入影响。对于读多写少的内容站,新增 1 到 3 个关键索引通常可以接受;对于订单、会员、日志频繁写入的站点,索引越多,INSERT 和 UPDATE 的成本越明显。更保守的做法是选择低峰期执行 DDL,并准备回滚脚本和备份点。关于备份和迁移,站内这篇 WordPress 网站迁移经验 可以作为上线前检查参考。

索引验证前后执行计划对比

五、实际案例:后台订单页从 3.2 秒降到 420ms

举一个脱敏案例。某独立站后台订单列表在活动日明显变慢,页面 TTFB 从平时 600ms 上升到 3.2 秒,MySQL 慢查询日志显示同一类订单筛选 SQL 在 15 分钟内出现 186 次,平均耗时 1.4 秒。服务器本身是 4 核 8GB,磁盘 I/O 没有打满,所以问题更像查询路径。

EXPLAIN 显示该 SQL 扫描约 38 万行,排序使用临时表。调整后增加了与订单状态和创建时间匹配的联合索引,并把后台默认筛选范围从“全部订单”改成“最近 90 天”。复测结果是平均查询耗时降到 180ms 左右,后台页面 TTFB 稳定在 420ms 到 650ms 之间。这里真正起作用的不是某个神奇参数,而是减少扫描行数和排序压力。

这类优化对 Hostease 用户也有现实意义:如果你使用美国或香港节点承载 WordPress 商城,中文客服和相对稳定的主机环境能帮助你更快确认资源侧问题;但数据库查询路径仍然要自己治理。换句话说,主机环境负责提供稳定地基,SQL 和索引设计决定楼梯是否顺畅。

六、上线后的观察指标和回滚边界

索引上线不是结束。至少观察 24 小时,重点看慢查询条数、平均响应时间、MySQL CPU、磁盘写入量和业务错误率。若站点有 CDN(内容分发网络)或缓存插件,也要区分缓存命中带来的表面提升,避免把缓存效果误判为数据库优化成功。

我建议把回滚边界写清楚:比如新增索引后,如果写入延迟超过 2 倍、复制延迟超过 60 秒、磁盘占用增长超过预估 30%,就暂停继续变更并回退。对于小团队来说,最怕的是“感觉更快了”但没有数字记录。哪怕只用一张简单表格记录变更前后 5 个指标,也比靠主观体验可靠。

如果后续还要继续优化,可以把方向分成三类:SQL 改写、缓存策略、资源扩容。SQL 改写适合解决扫描行数过大;缓存适合解决高频重复读;扩容适合 CPU、内存或磁盘长期逼近上限的情况。关于服务器选型,WHT 的 美国 VPS(虚拟专用服务器)选购指标 也可以作为资源侧判断参考。

总结:先证据,后动作

总结一下,MySQL 慢查询治理最怕两个极端:一种是只会加机器,另一种是只会加索引。前者可能多花钱,后者可能制造新的写入瓶颈。更稳的路线是先用日志确认慢 SQL,再用 EXPLAIN 判断索引命中情况,最后在接近真实数据量的环境里验证。

如果你需要处理线上站点卡顿,我的推荐动作很简单:先采样 15 分钟慢查询日志,挑出总耗时最高的前 5 类 SQL;再逐条看执行计划;最后只上线能解释清楚收益和风险的索引。这样做虽然比“重启数据库”慢一点,但它能留下可复盘的证据,也更适合长期运营的网站。

本文来自网络,不代表WHT中文站立场,转载请注明出处。https://hostease.webhostingtalk.cn/guides/mysql-slow-query-index-check/

作者: wht-he-admin

返回顶部