真正健康的组合是:type为eq_ref或ref、rows接近实际返回行数、key明确指向期望索引;type=ALL且key为空说明未走索引,type=ref但rows远大于结果集表明索引选择性差或统计过期。
看 type + rows + key 三者组合是否匹配查询意图
单看某一项容易误判。比如 type 是 ref 但 rows 高达 20 万,说明索引虽然被用了,但选择性极差;又比如 key 显示用了索引,但 type 是 all,那这个 key 很可能是“摆设”(优化器实际没走它)。真正健康的组合是:type 为 eq_ref 或 ref、rows 接近实际返回行数、key 明确指向你期望的索引名。
常见错配:
-
type = ALL且key为空 → 没走索引,查 WHERE 条件字段是否建索引、有无隐式转换 -
type = ref但rows远大于结果集(如查 5 行却扫 8 万)→ 索引字段顺序错或统计信息过期,运行ANALYZE TABLE再看 -
key有值但possible_keys更多 → 优化器选了次优索引,对比各索引字段覆盖度,优先保留能覆盖全部 WHERE 等值条件的联合索引
Filtered 值高但 key 为空,就是“假高效”
Filtered = 100% 不代表快,只代表服务器层没做额外过滤——但如果 key 是空的,说明存储引擎根本没用索引,全靠扫描后硬过滤,rows 就是真实扫描量。这种 100% 是“虚假繁荣”,性能瓶颈仍在 I/O。
实操判断点:
- 先确认
key列非空,再谈Filtered -
Filtered低于 30% 且rows较大 → 典型“索引覆盖不全”,比如 WHERE 里用了purchaser_id = ? AND order_status = ?,但索引只建在purchaser_id上,order_status只能在回表后逐行判断 - 想让
Filtered有意义,必须保证所有等值条件字段都落在同一联合索引的最左前缀内
Extra 出现 Using index condition 才算索引下推生效
Using index condition 出现在 Extra 列,说明 MySQL 把部分 WHERE 条件下推到了存储引擎层执行,减少了回表次数。但它依赖两个硬条件:索引字段顺序满足最左前缀、且该索引确实被选中(key 非空)。
容易忽略的细节:
- Navicat 默认不显示
key_len,右键执行计划表格 →「显示所有列」,确认key_len是否大于仅匹配等值条件所需长度(例如索引是(a,b,c),WHERE 含a = 1 AND b > 2,key_len应大于a单独长度) -
Using where和Using index condition都表示 Server 层参与过滤,但后者发生在引擎读索引时,IO 开销低得多 - MySQL 5.6 的 ICP 支持较弱,函数包裹字段(如
DATE(col))会直接禁用 ICP,升级到 5.7+ 更稳妥
PostgreSQL 的 GIN/GIST 索引要看是否真触发位图扫描
PostgreSQL 中 key 为空、type 是 Seq Scan,基本等于 GIN/GIST 索引没生效。GIN 多表现为 Bitmap Index Scan on xxx_gin_idx + Bitmap Heap Scan 组合,GIST 则更倾向 Index Scan using xxx_gist_idx。
关键验证动作:
- 确认 WHERE 使用了对应操作符:
@@(全文)、@>(JSONB 包含)、%(相似度),而不是=或LIKE 'abc%' - 检查索引定义是否带正确 ops:trigram 索引必须是
USING GIN (col gin_trgm_ops),缺gin_trgm_ops就不认 -
Rows Removed by Index Recheck高 → 索引过滤后仍需大量回表二次判断,说明索引区分度低或数据膨胀严重
rows 压到最小、是否让 type 落在 ref/eq_ref、是否让 Extra 里的开销项(filesort/temporary)消失。所有指标都要一起看,漏掉任意一环,优化就可能落空。











