不一定。key 为空仅表示优化器未选索引,原因包括索引失效、统计信息过期、WHERE 不满足最左前缀或数据量小导致全表扫描。

EXPLAIN 输出里 key 字段为空,是不是没走索引?
不一定。key 为 NULL 表示优化器最终没选择任何索引去扫描,但原因可能是多样的:索引失效、统计信息过期、WHERE 条件不匹配索引最左前缀、或者数据量太小优化器直接选了全表扫描。
实操建议:
- 先用
SHOW INDEX FROM table_name确认索引是否存在且字段顺序合理 - 检查 WHERE 中的列是否和索引定义严格对齐,比如索引是
(a, b, c),但查询只用了b = ?,那就不会命中 - 执行
ANALYZE TABLE table_name更新统计信息,尤其在大批量 INSERT/DELETE 后 - 用
FORCE INDEX临时验证索引是否真能用(如SELECT * FROM t FORCE INDEX (idx_a_b) WHERE a = 1)
ref 字段显示 const、func 还是某个列名,分别代表什么?
ref 描述的是索引查找时“拿什么值去比”。它直接反映查询条件与索引列的绑定关系,是判断是否高效使用索引的关键线索。
常见 ref 值含义:
-
const:WHERE 条件用了常量(如id = 123或主键等值查询),这是最理想的情况 - 列名(如
db.t.a):表示用另一张表或子查询的某列值来驱动当前索引查找,常见于 JOIN 场景 -
func:说明用了函数或表达式(如WHERE YEAR(created_at) = 2023),通常导致索引失效(除非是函数索引且 MySQL ≥ 8.0.13) -
NULL:没用到索引的查找逻辑,可能走了范围扫描(type 是range)或全表扫(ALL)
type = index 和 type = ALL 都是全扫描,差别在哪?
差别很大。type = index 是**索引全扫描**,即遍历的是索引 B+ 树的叶子节点,行数少、I/O 少、还能利用索引有序性;而 type = ALL 是**聚簇索引(数据页)全扫描**,要读取整行数据,开销大得多。
触发 index 的典型场景:
- 查询只涉及索引覆盖字段(
SELECT a,b FROM t WHERE a > 10,且(a,b)是联合索引) - ORDER BY 字段完全匹配索引最左前缀,且无额外排序(避免
Using filesort) - 注意:如果 WHERE 条件无法过滤,又没走覆盖索引,即使 type 是
index,性能也可能很差
为什么有时候加了索引,EXPLAIN 却显示 key 有值但 rows 很大?
key 有值只说明用了索引,不代表高效。rows 是优化器预估的扫描行数,它偏高往往意味着索引选择性差、或者查询条件落在低效的索引前缀上。
排查方向:
- 查
SELECT COUNT(DISTINCT col) / COUNT(*) FROM t,看该列选择性是否低于 0.1 —— 过低的索引不值得建 - 确认 WHERE 中是否用了
LIKE '%xxx'或OR连接非索引列,这类写法会让索引只能用上部分前缀 - 对比
EXPLAIN FORMAT=JSON输出里的used_range_access_method和range_details,看实际区间是否过大 - 留意 MySQL 版本差异:5.7 对隐式类型转换更敏感(如字符串字段查数字),8.0+ 在某些场景下会自动优化
真正难的不是看出 key 是否为空,而是看懂 ref 怎么来的、rows 为什么虚高、以及 type 后面藏着多少磁盘随机读 —— 这些细节不翻执行计划的 JSON 格式,光看传统列很容易误判。











