key为空但possible_keys有值,说明优化器识别到索引却主动跳过;主因包括最左前缀不匹配、函数/隐式转换、统计信息过期、索引选择性低,或Navicat还原时未导入索引定义。

执行计划里 key 为空但 possible_keys 有值,说明索引被“看见”却没被选中
这不是 Navicat 没建好索引,而是 MySQL 优化器主动跳过了它。常见真实原因包括:
-
WHERE条件字段顺序不匹配复合索引的最左前缀:比如索引是(status, category, created_at),但查询只写了WHERE category = 'A',这个索引完全不可用 - 对索引字段用了函数:
WHERE DATE(create_time) = '2025-01-01'→ 即使create_time有索引也失效 - 隐式类型转换:
user_id是BIGINT,但写成WHERE user_id = '123'(字符串字面量),MySQL 会放弃索引做全表扫描 - 统计信息过期:大批量写入后没运行
ANALYZE TABLE table_name,优化器误判“回表代价 > 全表扫描” - 索引选择性太低:比如
status只有'active'/'inactive'两个值,优化器认为走索引反而更慢
FORCE INDEX 是验证手段,不是长期解法
当你想快速确认“是不是索引本身能用”,可临时加 FORCE INDEX 强制走索引跑一遍:
SELECT * FROM orders FORCE INDEX(idx_user_id) WHERE user_id = 123;
但要注意:
- 它绕过了优化器决策,无法解决根本问题(比如字段类型不一致、函数包裹)
- 如果
idx_user_id字段类型和WHERE中的值不一致(如传字符串),FORCE INDEX仍不会生效 - PostgreSQL 不支持
FORCE INDEX,得用SET enable_seqscan = off或/*+ IndexScan(t idx_name) */Hint - 强制后若
rows明显下降且耗时缩短,说明索引结构没问题,下一步该更新统计信息或检查写法
建完索引必须验证是否真被用了
很多人点完 Navicat “设计表 → 索引 → 新增”就以为结束了,但执行计划里 key 还是空。真正要确认的步骤是:
- 运行
SHOW CREATE TABLE table_name,看输出里有没有你刚建的KEY idx_name (col1, col2) - 用
EXPLAIN FORMAT=TRADITIONAL查看完整执行计划,确认key列是否真的出现索引名 - 对比建索引前后
rows和实际执行耗时:如果rows没变小,说明索引根本没参与计算路径 - 检查
WHERE条件中是否有OR、!=、NOT IN等导致索引失效的逻辑
最容易被忽略的是:Navicat 还原数据库时默认不带索引 DDL,.nb3 备份文件尤其容易漏——你以为建好了,其实表结构里压根没有那条 KEY 定义。


















