用EXPLAIN定位慢查询索引问题需重点关注type、key、rows和Extra四列:type为ALL/index表示全表/索引扫描;key为NULL说明未命中索引;rows远大于实际返回行数反映索引选择性差;Extra含Using filesort/temporary表明排序分组未走索引。
EXPLAIN 快速定位慢查询的索引问题直接看 EXPLAIN 输出比盲猜有效得多。重点盯住 type、key、rows 和 Extra 这四列:
type 值为 ALL 或 index:说明走了全表扫描或全索引扫描,大概率缺合适索引key 为 NULL:没命中任何索引,哪怕表上有索引也可能是条件写法导致失效(比如对字段做函数操作)rows 明显大于实际返回行数:索引选择性差,或用了前缀索引但区分度低Extra 出现 Using filesort 或 Using temporary:ORDER BY / GROUP BY 没走索引,可能需要覆盖索引或调整排序字段顺序MySQL 的 B+ 树索引是按字段顺序逐级分裂的,等价于一个嵌套字典:{a: {b: {c: [...]}}}。这意味着:
b 或 c 字段,该索引完全无效a = ? AND b > ? 可以用到 a 和 b,但 c 后面的范围条件会截断索引下推a IN (?, ?) AND b = ? 时,b 无法继续用于索引查找(IN 是范围操作,会中断后续字段的等值匹配)
WHERE a = ? ORDER BY b, c → 索引建为 (a, b, c))SELECT * 和覆盖索引之间的性能鸿沟即使有索引,SELECT * 很可能让优化器放弃使用它——因为回表成本太高。覆盖索引指查询所需所有字段都在索引中,无需回主键索引查数据行。
EXPLAIN 中 Extra 出现 Using index(注意不是 Using index condition)(a, b) 建了索引,但查询写成 SELECT a, b, c FROM t WHERE a = ? → c 不在索引里,必然回表(a, b, c)),或明确只查索引包含的字段TEXT、BLOB)不能建索引,也不适合放进复合索引,否则索引体积暴涨这些写法会让 MySQL 直接跳过索引,哪怕字段上有完美索引也没用:
WHERE YEAR(create_time) = 2025 → 改成 WHERE create_time >= '2025-01-01' AND create_time
WHERE mobile = 13812345678(mobile 是 VARCHAR)→ 数字会被转成字符串再比较,触发全表扫描;应统一写成字符串 '13812345678'
WHERE name LIKE '%abc' → 无法使用 B+ 树的有序特性;若必须模糊查前缀,确保写成 LIKE 'abc%'
WHERE a = 1 OR b = 2 → 即使 a 和 b 都有索引,也可能全表扫;考虑拆成 UNION 或补上联合索引 (a,b) 并重写逻辑EXPLAIN SELECT id, name FROM user WHERE status = 1 AND created_at > '2025-01-01' ORDER BY score DESC;
如果这个查询慢,先看 EXPLAIN 是否走了 status 索引;如果没有,优先建联合索引 (status, created_at, score) —— 注意顺序不是随意排的,而是按「过滤→范围→排序」的层级来组织。漏掉任何一个环节,都可能让优化器重新评估成本并弃用索引。