key反映优化器选用的索引名,key_len表示实际使用的索引字节数,rows是基于统计信息估算的扫描行数。三者共同体现优化器预估的执行路径成本。

EXPLAIN 输出的 key、key_len、rows 字段到底在反映什么
EXPLAIN 不是执行器日志,而是优化器生成的「预估执行计划」。它不运行 SQL,只模拟优化器如何选择索引、估算扫描行数、决定连接顺序。其中:
-
key显示最终被选用的索引名——但注意,它可能是NULL(没走索引),也可能是PRIMARY(走了主键),还可能是idx_name(走了二级索引)。如果key和WHERE条件字段不匹配,大概率是索引失效或统计信息过期。 -
key_len表示实际用到的索引字节数,不是字段长度。比如VARCHAR(255)定义为 utf8mb4,单字符最多 4 字节,但若条件是WHERE name = 'abc',且name是联合索引第一列,则key_len可能是12(3 字符 × 4 字节),而不是 1020(255×4);若条件含前缀模糊匹配(LIKE 'abc%'),key_len仍会计算,但LIKE '%abc'就不会走索引,key_len为NULL。 -
rows是优化器基于表统计信息(INFORMATION_SCHEMA.STATISTICS或ANALYZE TABLE结果)估算的扫描行数,不是精确值。当rows远大于实际结果集(比如SELECT COUNT(*)只返回 10 行,但rows=98765),说明统计信息陈旧,需手动执行ANALYZE TABLE t_name更新。
type=ALL 和 type=range 的区别不只是“全表扫 vs 索引扫”
type 字段反映访问类型,但它的取值逻辑依赖于「是否用上索引」+「是否能做范围裁剪」+「是否覆盖查询所需字段」:
-
type=ALL:表示全表扫描,哪怕加了WHERE条件,只要没命中可用索引(如函数包裹字段:WHERE YEAR(create_time) = 2023),就会回退到ALL。 -
type=range:表示用了索引做范围扫描,但只适用于 B+Tree 索引的有序特性。例如WHERE id BETWEEN 100 AND 200、WHERE status IN (1,2)、WHERE name > 'zhang'。注意:如果联合索引是(a,b,c),而条件只有WHERE b = 2,则无法利用该索引的有序性,type仍是index或ALL,不是range。 - 容易忽略的一点:
type=index表示遍历整个索引树(按索引顺序全扫),比ALL快(因索引更小、顺序 I/O 更好),但如果SELECT *且索引非覆盖,仍要回表,性能未必优于ALL(尤其当数据页缓存差时)。
Extra 字段里 “Using filesort” 和 “Using temporary” 意味着什么
这两个提示直接暴露了排序和分组操作未走索引,而是由 server 层临时处理:
免费的开源程序长期以来,为中国的网上交易提供免费开源的网上商店系统一直是我们的初衷和努力奋斗的目标,希望大家一起把MvMmall网上商店系统的免费开源进行到底。2高效的执行效率由资深的开发团队设计,从系统架构,数据库优化,配以通过W3C验证的面页模板,全面提升页面显示速度和提高程序负载能力。3灵活的模板系统MvMmall网店系统程序代码与网页界面分离,灵活的模板方案,完全自定义模板,官方提供免费模
-
Using filesort:不是真写磁盘文件,而是 MySQL 在内存中用qsort排序,超限后才落磁盘。触发常见场景包括:ORDER BY字段不在索引最左前缀中、ORDER BY与WHERE条件用不同索引、ORDER BY a DESC, b ASC(混合方向,MySQL 8.0 前不支持索引满足)。 -
Using temporary:表示创建了内部临时表,通常出现在GROUP BY、DISTINCT、UNION或某些ORDER BY场景。如果GROUP BY字段没索引,或索引不能覆盖SELECT列(导致无法用松散索引扫描),就必现此提示。MySQL 5.7+ 默认用TempTable引擎(内存哈希表),但一旦超出tmp_table_size或含大字段(TEXT/BLOB),就会转成磁盘MyISAM表,性能断崖下跌。 - 二者常同时出现:
GROUP BY x ORDER BY y若x和y不在同一个索引里,既建临时表又额外排序。
为什么 ANALYZE TABLE 后 EXPLAIN 结果变了,但查询没变快
更新统计信息能让优化器选更合理的执行路径,但不保证性能提升——因为优化器只看成本模型,不看真实 I/O 延迟或并发争用:
- 统计信息准确了,优化器可能从走
idx_a改为走idx_b,但如果idx_b的叶子节点更稀疏(比如高基数字段 + 高碎片率),实际随机 I/O 次数反而上升。 - 优化器默认假设所有数据页都在 buffer pool 中,但生产环境常有冷数据。此时
rows=1000的range扫描,若对应 1000 个分散的磁盘页,比rows=5000的ALL扫描(顺序读)更慢。 - 真正要验证效果,得用
SELECT ... INTO DUMPFILE或sys.schema_table_statistics查看实际innodb_rows_read和handler_read_next,而不是只盯EXPLAIN的rows。
EXPLAIN FORMAT=TRADITIONAL SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE u.status = 1 AND o.created_at > '2024-01-01' ORDER BY o.amount DESC;
这类 JOIN 查询的执行计划,优化器决策链极长:先评估单表访问方式(u.status 是否有索引?o.created_at 是否有索引?),再估算 JOIN 代价(NLJ / BKA / Hash Join),最后判断 ORDER BY 能否复用某个索引的物理顺序。任何一个环节的统计偏差或隐式类型转换(比如 user_id 是 BIGINT,但 ON u.id = o.user_id 中某边被转成 DOUBLE),都可能让最终计划偏离预期。









