先确认慢查询真来自mysql而非其他环节:接口变慢时90%误判源于未定位瓶颈,需结合show processlist状态、innodb缓冲池命中率、链路追踪db耗时占比及performance schema数据综合判断,再决定是否执行explain。

怎么确认慢查询真来自MySQL而不是其他环节
先别急着开EXPLAIN,很多“慢SQL”根本不是数据库的问题。接口响应变长时,90%的误判都发生在没分清瓶颈归属就直奔数据库。
常见干扰源包括:网关超时、应用层循环调用、远程服务阻塞、缓存穿透、连接池耗尽。真正属于MySQL的慢,通常伴随以下信号:
-
SHOW PROCESSLIST里出现大量State为Sending data、Copying to tmp table或Sorting result的线程 - 监控中
innodb_buffer_pool_hit_ratio持续低于95%,或Threads_running突增 - 链路追踪(如SkyWalking)明确标出DB span耗时占比超过70%
如果slow_query_log里没记录,但performance_schema.events_statements_summary_by_digest里某条SQL的AVERAGE_TIMER_WAIT远高于其他,则大概率是短平快但高频的隐性慢查询,需用SYS.STMT_ANALYSIS聚合分析。
EXPLAIN里哪些字段直接暴露执行效率问题
EXPLAIN不是看有没有索引,而是看索引用得对不对、扫得少不少、额外开销大不大。
重点关注四列组合:type + key + rows + Extra:
-
type = ALL且rows > 1000:基本等于全表扫描,优先加索引或改条件 -
key = NULL但WHERE里有等值条件:检查是否对字段用了函数(如WHERE YEAR(create_time) = 2026),或类型隐式转换(varchar字段传int) -
rows预估数远大于实际返回行数(比如rows=50000但Rows_sent=1):说明索引选择性差,需要联合索引或调整条件顺序 -
Extra含Using filesort或Using temporary:ORDER BY或GROUP BY没走索引,不是加单列索引就能解决,得按排序字段+查询字段建覆盖索引
注意:filtered列低于10%时,即使type=ref也说明索引过滤效果极差,别被“用了索引”骗了。
为什么开了慢查询日志却找不到问题SQL
默认配置下,很多真实慢查询根本不会进日志——不是它不慢,而是你没配对参数。
生产环境最常漏掉的三个配置项:
-
min_examined_row_limit=1000:不设这个,小表查询(比如只查10行)哪怕执行2秒也不会记;但大表扫描10万行只花0.8秒,反而被放过 -
log_queries_not_using_indexes=OFF:设成ON会炸盘,但临时开启+配合min_examined_row_limit能抓到“快但不该快”的无索引查询 -
long_query_time设成1秒后,要确认是否生效:SELECT @@long_query_time,因为会话级变量可能覆盖全局设置
日志路径也要核对:SELECT @@slow_query_log_file,有些MySQL版本默认写到/var/lib/mysql/hostname-slow.log,不是/var/log/mysql/下。
索引优化最容易忽略的底层逻辑
加索引不是越多越好,更不是照着WHERE字段堆砌。真正卡住性能的,往往是这几个细节:
- 联合索引的字段顺序必须匹配查询条件的最左前缀,但
ORDER BY字段如果不在索引末尾,仍会触发Using filesort -
SELECT *导致回表,即使有索引也慢;改成只查需要的字段,配合覆盖索引(key列显示索引名且Extra含Using index)才能避免IO放大 - 字符串字段建索引要限制长度:
INDEX(name(191)),否则InnoDB可能因索引页过大拒绝使用 - 时间范围查询(
BETWEEN、>)只能用上索引的最右字段,左边字段必须是等值;否则范围之后的字段全部失效
最后提醒一句:EXPLAIN输出的rows是估算值,依赖表统计信息。如果刚大批量INSERT/DELETE过,记得ANALYZE TABLE刷新统计,否则执行计划可能严重失真。











