EXPLAIN中key为空但possible_keys有值,说明索引存在但未被优化器选用;常见原因包括WHERE不满足最左前缀、隐式类型转换、函数包裹字段、统计信息过期等。
EXPLAIN里key为空但possible_keys有值,说明索引存在但没被选中
这比“索引压根没建”更常见,也更难排查。navicat右键sql → “解释”后,如果key列是null而possible_keys列出索引名,代表优化器“看见”了索引,但主动放弃了它。
- 最常见原因是
WHERE条件不满足最左前缀:比如索引是(status, created_at),但查询写成WHERE created_at > '2026-01-01'——created_at不在最左,无法跳过status直接定位 -
隐式类型转换:字段是
VARCHAR,却传了数字字面量WHERE mobile = 13800138000,MySQL会把整列转为数字比较,索引失效 - 函数包裹:如
WHERE DATE(created_at) = '2026-07-27',哪怕created_at上有索引,也会触发全表扫描 - 统计信息过期:大批量导入或删除后未执行
ANALYZE TABLE,优化器误判该索引“选择性差”,宁可扫全表
SHOW INDEX确认索引存在,但EXPLAIN仍显示type=ALL
先跑SHOW INDEX FROM table_name,确认输出里真有你预期的索引名、字段顺序和类型。如果这里都对,但EXPLAIN还是type: ALL,问题大概率不在结构本身。
- 检查
WHERE是否用了OR连接非索引字段:例如WHERE status = 'paid' OR user_id IS NULL,只要user_id没索引,整个条件就无法走索引 - 联合索引中等值条件必须全出现:索引
(a, b, c),查询WHERE a = 1 AND c = 3只能用上a,c被跳过;而WHERE b = 2则完全不可用 - 字符集/排序规则不一致:
utf8mb4_0900_as_cs和utf8mb4_general_ci混用时,即使字段类型匹配,也可能绕过索引 - Navicat默认拉取全部结果:你写了
LIMIT 10,但它可能先让MySQL返回所有匹配行再本地截断——查50万行再取10条,网络和渲染都卡,看起来像“索引没用”
Navicat“设计表→索引”里看着正常,实际就是不生效
Navicat的图形界面只改元数据,不触发统计更新。哪怕你在“索引”标签页里勾选了字段、点了保存,SHOW INDEX能查到,EXPLAIN依然可能无视它。
- 添加/修改索引后,必须手动执行
ANALYZE TABLE table_name——这是强制刷新优化器“记忆”的唯一可靠方式 - 别用Navicat的「维护 → 重新优化表」:它执行的是
OPTIMIZE TABLE,会重建表+重排数据页,耗时长且锁表,对索引生效无直接帮助 - 某些Navicat 16.0.x版本(≤16.0.12)在解析
EXPLAIN输出时会丢掉key字段值,造成误判;验证方法:把同一SQL复制到命令行客户端执行EXPLAIN FORMAT=JSON SELECT ...,看key和used_columns是否真实为空 - 虚拟列索引(如JSON字段)必须是
STORED类型:Navicat表设计器里勾选“虚拟”时,默认生成VIRTUAL列,这种列不能建索引;得手动改DDL为ADD COLUMN v_item_id BIGINT AS (json_extract(params, '$.item_id')) STORED
PostgreSQL里看到Seq Scan,不是Navicat的问题
Navicat只是展示执行计划,真正决定走不走索引的是PostgreSQL优化器。频繁Seq Scan通常指向底层配置或查询逻辑问题。
-
random_page_cost值过高:SSD环境下仍用默认4.0,会让优化器认为随机IO比顺序IO贵得多,宁愿全表扫也不走索引;建议调低至1.1~1.3 - 统计信息不准:批量导入后没运行
ANALYZE table_name,优化器基于过时采样估算成本,可能错判索引更慢 - 查询返回数据过多:如
SELECT *匹配行数占全表15%以上,优化器会认为回表成本高于顺序扫描——这时加索引反而拖慢 - 函数导致索引失效:如
WHERE to_char(created_at, 'YYYY-MM') = '2026-07',需改写为范围查询或建函数索引CREATE INDEX idx_orders_month ON orders((to_char(created_at, 'YYYY-MM')))











