mysql是否使用索引取决于优化器基于成本估算的执行计划,而非“有索引就用”;常见失效场景包括高选择性差、函数运算、隐式转换、or条件未覆盖、范围查询后列无法利用等,explain中type、key、rows、extra字段是关键判断依据。

MySQL 并不总是使用索引,是否走索引取决于优化器的代价估算
MySQL 的查询优化器会为每个 SELECT(以及带 WHERE 的 UPDATE/DELETE)生成执行计划,是否使用索引不是“有索引就用”,而是基于统计信息估算全表扫描 vs 索引扫描 + 回表的 I/O 和 CPU 成本。常见导致**有索引却不用**的情况包括:
- 查询返回大量行(例如
WHERE status != 'done'匹配 95% 数据),优化器认为全表扫描更快 - 索引列在
WHERE中参与了函数或表达式运算,如WHERE YEAR(create_time) = 2023(create_time有索引也失效) - 隐式类型转换:比如
user_id是INT,但写成WHERE user_id = '123',MySQL 可能放弃索引 - 使用
OR连接多个条件,且并非所有字段都有联合索引覆盖,例如WHERE a = 1 OR b = 2,而只有a单列索引
用 EXPLAIN 看懂索引是否真的被用了
EXPLAIN 是判断索引使用情况最直接的方式,重点关注以下几列:
-
type:值为const、ref、range表示走了索引;ALL是全表扫描;index是索引全扫描(仍慢) -
key:显示实际使用的索引名,为NULL就没走索引 -
rows:优化器预估扫描行数,远大于实际结果集时,说明索引效率低或未生效 -
Extra:出现Using filesort或Using temporary往往意味着排序/分组无法利用索引完成
示例:
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'paid';若
key 显示 idx_user_status,且 type 是 ref,说明联合索引生效。联合索引的最左前缀原则不是“必须从第一列开始”,而是“连续匹配最左的列”
对联合索引 (a, b, c),以下 WHERE 条件能用上索引:
-
WHERE a = 1→ 用到a -
WHERE a = 1 AND b = 2→ 用到a,b -
WHERE a = 1 AND b = 2 AND c = 3→ 全部用到 -
WHERE a = 1 AND c = 3→ 只用到a,c无法跳过b使用 -
WHERE b = 2 AND c = 3→ 完全不走该索引(没有a)
注意:范围查询(>、、<code>BETWEEN、LIKE 'abc%')之后的列无法用于索引查找,例如 WHERE a = 1 AND b > 2 AND c = 3,只有 a,b 生效,c 不参与查找(但可能用于过滤)。
ORDER BY 和 GROUP BY 能否避免排序,关键看索引顺序是否匹配
如果 ORDER BY a, b,而索引是 (a, b),就能直接按索引物理顺序返回,无需 Using filesort;但如果索引是 (b, a) 或只有 (a),则大概率触发文件排序。
-
GROUP BY同理:索引(a, b)支持GROUP BY a, b或GROUP BY a,但不支持GROUP BY b - 升序/降序混合(如
ORDER BY a ASC, b DESC)在 MySQL 8.0 之前无法用索引,8.0+ 支持,但需建索引时显式声明:INDEX idx(a ASC, b DESC) - 覆盖索引(
SELECT字段全部包含在索引中)可避免回表,但前提是字段顺序和SELECT列表无关,只和WHERE/ORDER BY的匹配有关
索引不是越多越好,INSERT/UPDATE/DELETE 都要维护索引;真正难的是理解优化器怎么想——它不看“你写了什么”,只算“哪种路径代价更低”。一个 EXPLAIN 结果里 rows 偏高、key 为 NULL、Extra 里带 Using filesort,比任何理论都更值得立刻干预。











