explain的key非null不等于索引真正生效,须结合type(如all/index表示未有效过滤)、rows(接近总行数说明扫描量大)和extra(如using filesort表明排序未走索引)综合判断。

EXPLAIN 的 key 字段不等于索引真生效了
很多人看到 EXPLAIN 输出里 key 列有值,就以为索引“起作用了”。其实 key 只表示优化器“计划用哪个索引”,不是执行时真的靠它过滤了数据。真正判断过滤效果,得看另外三个字段:type、rows、Extra。
常见误判场景:
-
type是index或ALL:说明在遍历整个索引或整张表,没做有效行数削减 -
rows接近表总行数(比如 10 万行的表,rows=98234):即使key非空,也可能只是用来排序或覆盖,没减少扫描量 -
Extra出现Using filesort或Using temporary:常意味着索引无法满足ORDER BY或GROUP BY,被迫回表或建临时表
重点盯住 type 字段的访问级别
type 是判断索引过滤能力最直观的指标,从好到差大致是:const ≈ eq_ref > ref > range > index > ALL。只要掉到 range 以下,基本说明索引没起到预期的“定位+过滤”作用。
典型问题与对应原因:
- 本该走
ref却变成range:检查 WHERE 条件是否对索引列做了函数操作,例如WHERE YEAR(create_time) = 2023—— 这会让索引失效 - 联合索引只用上左边几列,但跳过了中间列:比如索引是
(a,b,c),而条件是WHERE a = 1 AND c = 3,则b之后的部分无法利用 -
type是index且rows很大:说明 MySQL 正在全量遍历二级索引,可能需要考虑覆盖索引,或收缩查询字段范围
当优化器选错索引时,FORCE INDEX 是快速验证手段
MySQL 优化器依赖统计信息选索引,但统计可能过期、不准,尤其在数据倾斜、小表 join 大表等场景下,它会“自信地选错”。这时加 FORCE INDEX 不是为了长期上线,而是为了快速验证:如果强制指定后 rows 显著下降、type 提升到 ref 或更好,那大概率是优化器误判,后续应更新统计信息或调整索引设计。
示例写法:
EXPLAIN SELECT * FROM orders FORCE INDEX (idx_customer_status) WHERE customer_id = 12345 AND status = 'paid';
rows 和 filtered 要一起看才反映真实过滤率
rows 是优化器估算的“需要扫描的行数”,但它不告诉你这些行里有多少被最终留下。filtered 字段(MySQL 5.7+)表示这个表在应用 WHERE 条件后,剩余行数占扫描行数的百分比。比如 rows=10000、filtered=10.00,说明只留下约 1000 行——过滤效率低,可能需要补条件或改索引顺序。
容易被忽略的点:
-
filtered值极低(如 rows 也不高:说明扫描少但条件太松,结果集稀疏,未必是索引问题,可能是业务逻辑本身如此 -
filtered接近 100% 但rows极高:说明索引没帮上忙,WHERE 条件根本没命中索引前缀,或者用了LIKE '%xxx'这类无法使用索引的写法
真正难的不是看懂单个字段,而是把 type、rows、filtered、Extra 放在一起交叉验证——同一句 SQL,在不同数据分布下,EXPLAIN 结果可能完全不同。











