ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

线上接口突然慢 5 倍?从慢查询日志、EXPLAIN 到索引修复,一次完整复盘 线上接口突然慢 5 倍从慢查询日志、EXPLAIN 到索引修复一次完整复盘导读某个列表接口稳定跑了半年某天突然从 80ms 涨到 400ms监控报警。没有上线新代码、没有数据暴涨就是慢。最后靠慢查询日志 EXPLAIN 定位到一个索引失效改完恢复 70ms。这篇把整个排查过程复盘一遍。先描述现场。零工系统的岗位列表接口日常 P99 在 80ms 左右某天监控显示突然涨到 400ms持续不退。当天没有发版、没有大促、数据量也没暴增这就很蹊跷——八成是执行计划变了。排查的第一步不是看代码是看慢查询日志。第一步开慢查询日志用 mysqldumpslow 找凶手生产库默认慢查询阈值是 10 秒显然没开够。先确认现状再调阈值-- 查看当前配置SHOWVARIABLESLIKEslow_query_log%;SHOWVARIABLESLIKElong_query_time;然后把阈值降到 0.5 秒方便抓这种不算太慢但明显劣化的查询SETGLOBALslow_query_logON;SETGLOBALlong_query_time0.5;SETGLOBALlog_queries_not_using_indexesON;跑个半小时后用mysqldumpslow按平均耗时排序直接看 TOP 语句mysqldumpslow-sat-t10/var/lib/mysql/mysql-slow.log输出里job_post的列表查询赫然在列平均执行 0.4 秒、出现了 3000 次。这就是凶手。第二步EXPLAIN 看执行计划问题一眼暴露把慢 SQL 拿出来 EXPLAINEXPLAINSELECTid,title,salary,city,create_timeFROMjob_postWHEREcity赣州ANDpost_status1ORDERBYcreate_timeDESCLIMIT20;执行计划返回type: ALL -- 全表扫描这是根源 key: NULL -- 没有用任何索引 rows: 235000 -- 扫了 23 万行全表扫描 23 万行400ms 一点不冤。但奇怪的是表上明明有(city, post_status)的联合索引为什么没走第三步定位索引失效问题出在排序继续深挖。把 ORDER BY 去掉再 EXPLAINEXPLAINSELECTid,titleFROMjob_postWHEREcity赣州ANDpost_status1;这次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()但传参时某条链路把空字符串传成了 NULLWHERE city NULL永远不成立……等等这不对。定位思路真正的坑是字符集排序规则不一致。job_post.city建表时是utf8mb4_general_ci而另一个表 JOIN 出来的临时列是utf8mb4_0900_ai_ciMySQL 做比较时字符集排序规则不同会导致索引失效直接退化成全表。最终解决用SHOW FULL COLUMNS FROM job_post LIKE city对比两边排序规则统一改成utf8mb4_general_ci后EXPLAIN 立刻type: refrows: 130。恢复 70ms报警解除。可直接复用的清单接口劣化先看慢查询日志mysqldumpslow -s at按耗时排EXPLAIN 重点看三列type别是 ALL、key别是 NULL、rows别是十几万联合索引排序字段放等值条件之后方向要匹配 ORDER BYMySQL 8 用降序索引老版本退而求其次用普通联合索引WHERE 字段字符集排序规则不一致也会让索引失效统一成一种排查链路最后一步用SHOW FULL COLUMNS核对排序规则。这套 SQL 优化实践支撑着零工系统的岗位查询。相关项目源码https://gitee.com/gzqkl/xllg
返回列表