联合索引遵循最左前缀原则,即查询必须从索引最左列开始且不跳过中间列,否则索引部分或全部失效;例如索引(a,b,c)中,仅b=或c=条件无法使用该索引。

key 为 NULL 就代表没走索引?
不一定。key 字段为 NULL 表示优化器最终没选择任何索引,但原因可能是多样的:查询条件未覆盖索引最左前缀、隐式类型转换、函数包裹字段(如 WHERE YEAR(create_time) = 2024)、或索引统计信息过期导致优化器误判。不要只看 key,要结合 possible_keys 是否为空——如果 possible_keys 有值而 key 是 NULL,说明索引存在但被跳过了。
常见踩坑点:
- 对索引字段用
LIKE '%abc',导致无法使用 B+ 树索引 - WHERE 中混用不同字符集的列做比较,触发隐式转换
- 联合索引
(a, b, c),但查询只用b = ?或c = ?,无法命中
rows 值大就一定慢?
rows 是优化器基于统计信息估算的「扫描行数」,不是实际执行时的物理 I/O 次数,也不等于返回结果行数。它只是性能预警信号:当 rows 显著大于你预期返回的行数(比如查 1 条却显示 rows=50000),大概率说明索引没生效或用了低效访问类型(如 type=ALL)。
注意几个关键事实:
-
rows是估算值,受ANALYZE TABLE更新频率影响;表数据剧烈变化后不刷新统计,rows可能严重失真 - 即使
rows很小,若type=index(全索引扫描),仍可能比type=range+ 更大rows更慢 - 多表 JOIN 时,
rows是对「当前表」的单表预估,不是整个查询总扫描量
如何快速判断是否命中联合索引的最左前缀
看 key_len 和 key 的组合。例如联合索引 idx_user_status_created 是 (user_id, status, created_at),三个字段类型分别是 BIGINT(8 字节)、TINYINT(1 字节)、DATETIME(5 字节,默认无精度扩展)。
执行 EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 1; 后:
- 若
key显示idx_user_status_created,且key_len = 9,说明只用了前两个字段(8+1),符合最左前缀 - 若
key_len = 8,说明只命中了user_id,status条件虽写上但未参与索引过滤(比如用了 OR、或该字段有 NULL 值且索引未设 NOT NULL) -
key_len不是整除关系?检查字段是否允许 NULL(会额外占 1 字节)或用了变长类型(如VARCHAR加上长度头)
Extra 字段里哪些提示直接暴露索引失效
Extra 是诊断索引是否“真正起效”的最后一道关卡。以下值出现即表示索引能力打折甚至归零:
-
Using filesort:ORDER BY 无法利用索引排序,需额外内存/磁盘排序 -
Using temporary:GROUP BY 或 DISTINCT 触发临时表,常因缺少覆盖索引或排序字段不在索引中 -
Using index condition:说明用了 ICP(Index Condition Pushdown),是好事;但若同时出现Using where,意味着索引只做过滤,剩余条件仍要回表 -
Using index(单独出现):恭喜,是覆盖索引,无需回表——这是索引利用的最高形态
真正麻烦的是 Using where; Using index 这种组合:看起来走了索引,但 Using where 暗示仍有部分条件在 Server 层过滤,说明索引设计没完全匹配查询需求。











