EXPLAIN 是判断索引是否生效的第一现场,key 为 NULL 或 type 为 ALL 表示未用索引;联合索引需满足最左前缀;performance_schema.table_io_waits_summary_by_index_usage 可查真实使用频次;慢查询日志结合 Rows_examined 分析实际效率;SHOW INDEX 的 Cardinality 仅为估算值,需 ANALYZE TABLE 更新。

EXPLAIN 是判断索引是否生效的第一现场
直接在查询前加 EXPLAIN,就能看到 MySQL 实际怎么走的索引——不是“有没有建”,而是“这次查有没有用上”。
-
key字段为NULL?说明这条 SQL 完全没走索引,哪怕表上有几十个索引也没用 -
type是ALL?就是全表扫描,得立刻查原因;range、ref、eq_ref才算正常走索引 -
Extra出现Using filesort或Using temporary?ORDER BY 或 GROUP BY 没命中索引排序,性能隐患很大 - 联合索引要注意最左前缀:查
WHERE a = 1 AND b = 2能用INDEX(a,b);但只查b = 2就完全失效
示例:EXPLAIN SELECT * FROM users WHERE email = 'a@b.com' AND status = 1; ——重点盯 key 是否是你期望的 idx_email_status,别光看建了没建。
performance_schema 查真实访问频次,不是“理论能用”而是“真被用了”
MySQL 5.7+ 的 performance_schema.table_io_waits_summary_by_index_usage 表,记录的是运行时每个索引被读/写调用的真实次数,比 EXPLAIN 更反映长期实际负载。
-
COUNT_FETCH = 0且持续多天?这个索引大概率是“僵尸索引”,可标记待清理 -
COUNT_INSERT高但COUNT_FETCH极低?说明它主要拖慢写入,却几乎不加速查询,性价比极差 - 注意过滤系统库:
WHERE OBJECT_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema'),否则结果会被干扰 - 该功能默认可能关闭,需确认
performance_schema已启用,且相关消费者已打开(如events_statements_history_long)
查法:SELECT OBJECT_NAME AS table_name, INDEX_NAME, COUNT_FETCH FROM performance_schema.table_io_waits_summary_by_index_usage WHERE OBJECT_SCHEMA = 'your_db' AND COUNT_FETCH = 0;
慢查询日志里藏着“假装走索引”的陷阱
有些查询虽然 EXPLAIN 显示走了索引,但实际效率极低(比如扫了几百万行),这类问题只靠执行计划看不出,必须结合慢日志抓出来。
- 务必开启
log_queries_not_using_indexes = ON,否则连“根本没走索引”的语句都漏掉 -
long_query_time别设成 2 秒这种“看起来合理”的值——业务高峰期响应稍慢就可能掩盖问题,建议从 0.1 或 0.5 秒起步 - 日志里出现
Rows_examined: 124892却只返回几条?说明索引区分度差或条件没利用好,Cardinality值可能严重失真 - 用
pt-query-digest分析比手动翻日志高效得多,重点关注Rows_examined/Rows_sent比值过高的查询
配置示例:SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 0.5; SET GLOBAL log_queries_not_using_indexes = ON;
SHOW INDEX 和 Cardinality 不是“健康证明”,而是需要交叉验证的线索
SHOW INDEX FROM table_name 返回的 Cardinality 是估算值,不是实时准确计数,单独看它容易误判索引质量。
-
Cardinality接近表总行数?说明该列区分度高,索引大概率有效;若只有几百,而表有百万行?这个索引很可能形同虚设 - 该值不会自动实时更新,大表增删改后可能严重滞后,需手动执行
ANALYZE TABLE table_name;触发重统计 -
Seq_in_index对联合索引至关重要:如果查WHERE b = 2 AND c = 3,而索引是INDEX(a,b,c),那根本用不上——顺序错,等于没建 - 别忽略
Sub_part:前缀索引(如INDEX(name(10)))只对前 10 个字符有效,WHERE name LIKE 'abc%'能用,但= 'abcdef'可能因截断无法精确匹配
查法:SHOW INDEX FROM orders\G(用 \G 垂直显示更易读),重点对比 Cardinality 与 SELECT COUNT(*) FROM orders; 的量级关系。
真正难的不是查出索引有没有用,而是搞清“为什么这次没用”——可能是隐式类型转换、函数包裹字段、OR 条件拆分、统计信息过期,或者优化器干脆觉得全表扫描更快。这些细节藏在 EXPLAIN 的 type 和 Extra 里,也藏在慢日志的 Rows_examined 里,得一条一条对齐看。



















