一条 SELECT 平时 10 毫秒,某天突然跑 8 秒,加索引也不知道加在哪,这时候就该看 mysql 执行计划了。在查询前面加 EXPLAIN 关键字就能拿到:
EXPLAIN SELECT * FROM orders WHERE user_id = 42;输出是一张表,列不少,日常真正要盯的就几个。id 相同的行自上而下执行,出现子查询时 id 会不同,数字越大越先执行。
先看 type,再看 key
type 表示访问方式,性能从好到差大致是 system、const、eq_ref、ref、range、index、ALL。const 是按主键或唯一索引取单行,ref 是普通索引等值匹配,range 是范围扫描,ALL 就是全表扫描,线上大表出现 ALL 基本是事故预备,先考虑建索引。key 列显示实际用到的索引;possible_keys 里有候选但 key 是 NULL,多半是统计信息不准,或者条件让索引失效了,比如对索引列套了函数、发生隐式类型转换。
rows 是优化器估算的扫描行数,几百万的表这个数字上万就要警惕。key_len 能看出索引用了几列,联合索引 (a, b, c) 只用到 a 和 b 时 key_len 会短一截,据此判断复合索引有没有用满。
Extra 里的信号
- Using index:覆盖索引,查询全在索引里完成,不回表,是好信号。
- Using filesort:需要额外排序,order by 的字段不在索引里时常出现,数据量大要处理。
- Using temporary:用了临时表,group by 或 distinct 容易带出来,通常伴随慢查询。
MySQL 8.0 还可以用 EXPLAIN ANALYZE,它会真跑一遍并给出每一步实际耗时和行数,比纯估算直观得多,注意它真的会执行语句,写 DELETE 前想清楚。
实操顺序建议:type 出现 ALL 就补索引,补完看 key 有没有走,rows 降不下来再查 Extra。执行计划是调优的起点,每次改完索引重新跑一遍执行计划做对比,别凭感觉上线。