☰
线上接口突然慢 5 倍?从慢查询日志、EXPLAIN 到索引修复,一次完整复盘
2026/10/8 5:51:28 网站建设 项目流程

线上接口突然慢 5 倍?从慢查询日志、EXPLAIN 到索引修复,一次完整复盘

导读:某个列表接口稳定跑了半年,某天突然从 80ms 涨到 400ms,监控报警。没有上线新代码、没有数据暴涨,就是慢。最后靠慢查询日志 + EXPLAIN 定位到一个索引失效,改完恢复 70ms。这篇把整个排查过程复盘一遍。

先描述现场。零工系统的"岗位列表"接口,日常 P99 在 80ms 左右,某天监控显示突然涨到 400ms,持续不退。当天没有发版、没有大促、数据量也没暴增,这就很蹊跷——八成是执行计划变了。

排查的第一步不是看代码,是看慢查询日志。

第一步:开慢查询日志,用 mysqldumpslow 找凶手

生产库默认慢查询阈值是 10 秒,显然没开够。先确认现状,再调阈值:

-- 查看当前配置SHOWVARIABLESLIKE'slow_query_log%';SHOWVARIABLESLIKE'long_query_time';

然后把阈值降到 0.5 秒,方便抓这种"不算太慢但明显劣化"的查询:

SETGLOBALslow_query_log=ON;SETGLOBALlong_query_time=0.5;SETGLOBALlog_queries_not_using_indexes=ON;

跑个半小时后,用mysqldumpslow按平均耗时排序,直接看 TOP 语句:

mysqldumpslow-sat-t10/var/lib/mysql/mysql-slow.log

输出里job_post的列表查询赫然在列,平均执行 0.4 秒、出现了 3000+ 次。这就是凶手。

第二步:EXPLAIN 看执行计划,问题一眼暴露

把慢 SQL 拿出来 EXPLAIN:

EXPLAINSELECTid,title,salary,city,create_timeFROMjob_postWHEREcity='赣州'ANDpost_status=1ORDERBYcreate_timeDESCLIMIT20;

执行计划返回:

type: ALL -- 全表扫描,这是根源 key: NULL -- 没有用任何索引 rows: 235000 -- 扫了 23 万行

全表扫描 + 23 万行,400ms 一点不冤。但奇怪的是,表上明明有(city, post_status)的联合索引,为什么没走?

第三步:定位索引失效,问题出在排序

继续深挖。把 ORDER BY 去掉再 EXPLAIN:

EXPLAINSELECTid,titleFROMjob_postWHEREcity='赣州'ANDpost_status=1;

这次type: ref,走了索引。问题就出在ORDER BY create_time DESC上。

原来的联合索引是(city, post_status),排序字段create_time不在索引里。MySQL 有两种选择:先按索引过滤出结果,再临时表 filesort;或者干脆全表扫。数据量一大,优化器觉得全表扫 + 自己排反而更"划算",就走了ALL。

解法不是加一个(city, post_status, create_time)联合索引就完事——排序字段要放在等值条件的后面,而且 DESC 方向要匹配:

ALTERTABLEjob_postADDINDEXidx_city_status_time(city,post_status,create_timeDESC);

MySQL 8 支持降序索引,8.0 之前的老版本加不了DESC,那就加普通(city, post_status, create_time),让排序走索引正序扫描,效果一样。

踩坑记录:索引加了,还是没走

问题现象:按上面方案加了联合索引,EXPLAIN 一看key还是 NULL,索引根本没被用上。

排查过程:检查发现慢 SQL 的 WHERE 里city是从接口参数拼进来的,类型是 VARCHAR,而表里city字段也是 VARCHAR,看起来没毛病。再细看,接口代码里对city做了String.trim(),但传参时某条链路把空字符串传成了 NULL,WHERE city = NULL永远不成立……等等,这不对。

定位思路:真正的坑是字符集排序规则不一致。job_post.city建表时是utf8mb4_general_ci,而另一个表 JOIN 出来的临时列是utf8mb4_0900_ai_ci,MySQL 做比较时字符集排序规则不同会导致索引失效,直接退化成全表。

最终解决:用SHOW FULL COLUMNS FROM job_post LIKE 'city'对比两边排序规则,统一改成utf8mb4_general_ci后,EXPLAIN 立刻type: ref+rows: 130。恢复 70ms,报警解除。

可直接复用的清单

  • 接口劣化先看慢查询日志,mysqldumpslow -s at按耗时排;
  • EXPLAIN 重点看三列:type(别是 ALL)、key(别是 NULL)、rows(别是十几万);
  • 联合索引排序字段放等值条件之后,方向要匹配 ORDER BY;
  • MySQL 8 用降序索引,老版本退而求其次用普通联合索引;
  • WHERE 字段字符集排序规则不一致也会让索引失效,统一成一种;
  • 排查链路最后一步用SHOW FULL COLUMNS核对排序规则。

这套 SQL 优化实践支撑着零工系统的岗位查询。相关项目源码:https://gitee.com/gzqkl/xllg

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询