MySQL 慢查询排查别只看索引:从 SQL 到执行计划的实际顺序

MySQL 慢查询排查别只看索引:从 SQL 到执行计划的实际顺序

一提到 MySQL 慢查询,很多人的第一反应就是“是不是没加索引”。这个判断不算错,但经常不够用。因为线上慢查询很多时候不是单纯缺索引,而是 SQL 写法、数据分布、分页方式、回表成本和执行计划一起叠加出来的问题。

如果一上来就盯着“建哪个索引”,排查经常会走偏。更稳的顺序应该是先理解查询在做什么,再看执行器到底怎么跑。

第一步先确认:到底是偶发慢,还是稳定慢

慢查询首先要分类型:

  1. 每次都慢
  2. 流量高时变慢
  3. 数据量大后才慢
  4. 偶发性抖一下

这几种情况背后的原因可能完全不同。

如果每次都慢,更像是 SQL 本身有问题;如果只是高峰期变慢,就要考虑锁竞争、IO 压力、缓存命中率和并发量;如果只是数据大了以后才慢,通常要重点看扫描范围和分页方式。

第二步别急着改,先把 SQL 还原完整

很多排查低效的原因是,大家手里拿到的不是完整 SQL,而是一段业务代码、一个 ORM 调用或者一条被日志截断的语句。

更稳的做法是先把下面这些信息拿全:

  1. 完整 SQL
  2. 实际参数
  3. 执行时间
  4. 返回行数
  5. 查询发生时的数据规模

没有这些上下文,后面看执行计划也容易误判。

第三步再看执行计划,而不是一上来猜

EXPLAIN 的价值不是让你看几个关键词,而是帮助你判断:

  1. 用没用到索引
  2. 扫了多少行
  3. 有没有临时表
  4. 有没有文件排序
  5. join 顺序是不是合理

尤其是扫描行数。很多 SQL 表面看起来用了索引,但如果扫描范围依然很大,整体还是会慢。

只会加索引,往往会越加越乱

中小项目很常见的一种坏味道是:哪条 SQL 慢,就给那条 SQL 再补一个索引。时间一长,索引越来越多,写入成本升高,维护也越来越难。

更合理的思路通常是先问:

  1. 这个查询是不是高频
  2. 这个字段组合是不是稳定
  3. 这个排序和过滤能不能共用一个索引
  4. 加了之后会不会和已有索引重复

索引不是越多越好,而是越贴近真实查询路径越有价值。

分页慢很多时候不是数据库“坏了”,而是偏移量太大

不少列表接口前面都很快,翻到后几页突然就开始慢。这个问题很常见,尤其是在内容站、后台列表、订单列表这种场景里。

原因往往不是单条记录难查,而是:

  1. `LIMIT offset, size` 的 offset 太大
  2. 数据库为了跳过前面的记录,依然要扫描很多行

这时候继续补索引帮助通常有限,更有效的办法往往是:

  1. 用基于游标的翻页
  2. 用上次最后一条 ID 做条件翻页
  3. 减少深分页需求

排查慢查询时别忘了回表成本

有些查询看起来已经命中索引了,但依然不快,原因可能是回表成本高。

典型情况是:

  1. 先通过二级索引筛到一批主键
  2. 再回主表取更多列
  3. 数据量一大,回表次数就开始明显影响性能

这时候就要看能不能通过覆盖索引、减少返回列、改写查询方式,把成本压下来。

排序和聚合也很容易把查询拖慢

很多慢查询并不是慢在 where,而是慢在:

  1. `order by`
  2. `group by`
  3. `distinct`
  4. 聚合统计

如果排序字段和过滤条件的索引顺序不匹配,数据库就很可能退回到额外排序。看起来像只是“多排一下”,实际在数据量上来之后差别会很大。

一个更适合中小项目的排查顺序

如果你手里有一条慢 SQL,我更建议按下面这个顺序来看:

  1. 先确认是不是稳定复现
  2. 拿到完整 SQL 和真实参数
  3. 看返回行数和数据规模
  4. 用 `EXPLAIN` 看扫描范围和执行路径
  5. 再判断是索引、分页、排序还是回表的问题
  6. 最后再决定是改 SQL、补索引还是改分页方式

这样做最大的好处是,你不会一上来就把“慢查询优化”简化成“补索引”。

结语

MySQL 慢查询排查真正重要的,不是背多少优化口诀,而是知道先看什么、后改什么。把 SQL 还原清楚、把执行计划看明白,再去决定是索引、分页还是排序的问题,优化动作才更稳。

对中小项目来说,少走几次“盲加索引”的弯路,往往比多会几条数据库术语更有用。