判断索引无效的最直接依据是EXPLAIN中key为NULL且本应走索引;MySQL 8.0+可通过sys.schema_unused_indexes和schema_redundant_indexes识别未用及冗余索引。

怎么判断一个 MySQL 索引是不是无效的
索引无效,不是指语法错误或建不成功,而是它在实际查询中根本没被优化器选中,或者只在极少数边缘场景触发。最直接的依据是 EXPLAIN 结果里 key 字段为 NULL,且该列本应走索引。
- 常见现象:WHERE 条件含函数(如
WHERE YEAR(created_at) = 2023),导致索引失效 - 隐式类型转换(如
WHERE user_id = '123',而user_id是 INT)也会让索引失效 - 前缀索引未覆盖查询条件(如对
email建了INDEX(email(10)),但查询用的是WHERE email = 'a@b.com'—— 看似匹配,实则因截断无法做等值比较) - 联合索引顺序错位(
INDEX(a,b,c)支持WHERE a=1 AND b=2,但不支持WHERE b=2 AND c=3)
MySQL 自带哪些工具能查冗余索引
8.0+ 版本起,sys 库提供了现成视图:sys.schema_redundant_indexes 和 sys.schema_unused_indexes,它们基于 performance_schema 的统计和索引结构分析生成结果,比人工扫表靠谱得多。
-
schema_unused_indexes依赖performance_schema开启且至少运行过一段时间(默认不记录历史查询,需确保events_statements_history_long或相关消费者已启用) -
schema_redundant_indexes不依赖运行时数据,纯靠 DDL 分析,比如INDEX(a)和INDEX(a,b)共存时,前者大概率冗余 - 注意:这两个视图不会标记“部分冗余”的联合索引(如
INDEX(a,b)和INDEX(a,b,c,d)并存,它可能只报前者冗余,但忽略后者是否真被用到)
执行清理前必须确认的三件事
删索引不是删文件,但后果可能比删错数据还难回滚 —— 尤其在线上 OLTP 场景下。
- 确认该索引没被任何慢查询、定时任务、报表 SQL 或 ORM 框架的隐式查询依赖(有些框架会自动生成
SELECT COUNT(*)或EXISTS,它们可能依赖某个看似“没用”的索引加速) - 检查
INFORMATION_SCHEMA.STATISTICS中SEQ_IN_INDEX和CARDINALITY,避免误删高基数单列索引(比如status列只有 3 个值,但建了索引,CARDINALITY接近 3,基本无筛选价值) - 务必在低峰期操作,并提前在从库或测试环境验证:删掉后跑一遍核心业务 SQL 的
EXPLAIN,对比key和rows是否恶化
一个轻量但够用的无用索引识别脚本
下面这个 SQL 不依赖 sys 库,兼容 5.7+,重点抓两类:完全未被使用的索引 + 可被联合索引覆盖的单列索引。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
SELECT
t.TABLE_SCHEMA,
t.TABLE_NAME,
s.INDEX_NAME,
s.COLUMN_NAME,
s.SEQ_IN_INDEX,
s.INDEX_TYPE,
IF(s.CARDINALITY = 0, 'low_card', 'normal') AS card_level
FROM INFORMATION_SCHEMA.STATISTICS s
JOIN INFORMATION_SCHEMA.TABLES t
ON s.TABLE_SCHEMA = t.TABLE_SCHEMA AND s.TABLE_NAME = t.TABLE_NAME
WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
AND s.NON_UNIQUE = 1
AND NOT EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS s2
WHERE s2.TABLE_SCHEMA = s.TABLE_SCHEMA
AND s2.TABLE_NAME = s.TABLE_NAME
AND s2.INDEX_NAME != s.INDEX_NAME
AND s2.COLUMN_NAME = s.COLUMN_NAME
AND s2.SEQ_IN_INDEX = 1
AND s2.INDEX_NAME LIKE CONCAT(s.INDEX_NAME, '%')
)
ORDER BY t.TABLE_SCHEMA, t.TABLE_NAME, s.INDEX_NAME;
这个脚本不保证 100% 准确,但它能快速筛出「大概率可删」的候选索引。真正决定删哪个,还得结合 sys.schema_unused_indexes 的运行时数据和业务日志交叉验证。
复杂点在于:有些索引在白天不用,但在凌晨备份或归档脚本里被重度依赖;还有些索引看着冗余,实则是为避免锁升级而刻意保留的。这些没法靠 SQL 自动识别,得翻代码、问同事、看监控。










