key为空但possible_keys有值,说明索引存在却被优化器跳过:常见原因包括WHERE未匹配最左前缀、字段类型隐式转换、或数据量小使全表扫描成本更低。
执行计划中 key 字段为空,但 possible_keys 有值
这说明优化器“看到”了索引,却没选它——不是没建,而是被跳过了。常见原因有三个:where 条件没匹配索引最左前缀、字段类型隐式转换、或者数据量太小导致优化器认为全表扫描更快。
比如你建了联合索引 (user_id, status, created_at),但查询只写了 WHERE status = 'active',user_id 没出现,这个索引就完全用不上。
再比如 user_id 是 INT 类型,而你在条件里写成 WHERE user_id = '123'(加了引号),MySQL 会做隐式转换,索引失效。
还有种情况:表只有几百行,优化器直接走 type: ALL,因为读索引+回表比扫一遍还慢——这不是 bug,是成本估算结果。
rows 值极小但依然没走索引
当 rows 显示为 1 或个位数,key 却是 NULL,大概率是统计信息过期或不准。InnoDB 的索引选择依赖 information_schema.STATISTICS 和采样数据,如果长期没更新,优化器可能误判索引区分度低。
手动刷新统计信息是最直接的验证方式:
ANALYZE TABLE orders;
注意:不要在高峰期执行,ANALYZE TABLE 会加表级读锁(MySQL 5.7+ InnoDB 默认轻量级采样,影响较小,但仍需留意)。
另外,某些 Navicat 版本(≤16.0)在解析 EXPLAIN 输出时会丢掉 key 字段,尤其开启 performance_schema 后。升级到 Navicat 17+ 或改用命令行执行 EXPLAIN FORMAT=JSON SELECT ... 可绕过该问题。
索引存在但执行计划显示 Using index condition 却不走主键
Using index condition 表示用了 ICP(Index Condition Pushdown),这是好现象,说明索引下推生效;但如果预期是走主键索引(比如 WHERE id = ?)却显示 type: ref + key: idx_user_status,就要检查是否真有 PRIMARY KEY(id),以及有没有意外的 FORCE INDEX 或优化器提示干扰。
更隐蔽的情况是:该表被 CREATE TABLE AS SELECT 或导入重建过,主键丢失,只剩普通索引。用 SHOW CREATE TABLE t; 看输出里是否有 PRIMARY KEY 定义。
还有一种容易被忽略的点:Navicat 的「解释」功能对带变量的语句(如 SELECT * FROM t WHERE id = ?)有时会降级为预估计划,而非真实执行路径。把 ? 替换成具体值再 EXPLAIN,结果才可靠。
为什么 Navicat 里点「解释」后 key 列始终为空
不是所有空 key 都代表索引失效。先确认当前连接的数据库类型图标是否和语句语法一致——比如在 PostgreSQL 连接里执行 MySQL 风格的 LIMIT 10,PostgreSQL 驱动会拒绝生成执行计划,返回空结果。
其次检查 Navicat 首选项:首选项 → 查询 中是否勾选了 Use legacy explain format。勾选后会强制使用旧解析逻辑,很多新字段(包括 key)会被忽略。关掉它,重启查询窗口。
最后,如果你刚恢复过数据库(比如从 .sql 文件导入),部分版本的 Navicat 会缓存旧的元数据,导致索引列表和实际不一致。按 Ctrl+Shift+R(Windows/Linux)或 Cmd+Shift+R(macOS)强制重载元数据,再试一次 EXPLAIN。











