sys.schema_unused_indexes不可靠,因它仅凭count_fetch=0判定索引未使用,忽略写操作依赖、查询类型差异、采样周期及performance_schema配置影响,误删可能导致唯一约束失效、外键锁表或全表扫描。

MySQL 8.0+ 才有 sys.schema_unused_indexes,它只反映“从未被 SELECT 用过”的索引,不包括 INSERT/UPDATE/DELETE 场景,且默认不启用 sys schema 的相关视图。
为什么 sys.schema_unused_indexes 不可靠
这个视图底层依赖 performance_schema.table_io_waits_summary_by_index_usage,但它的过滤逻辑太宽松:只要某索引在 COUNT_FETCH = 0,就列为“unused”。问题在于:
- 它完全忽略写操作(
COUNT_INSERT/COUNT_UPDATE高但COUNT_FETCH = 0的索引,可能正承担唯一约束或外键检查) - 它不区分查询类型——慢查询里没走索引的 SQL,不会被它捕获;而高频小查询即使走了索引,也可能因采样周期短未计入
- 如果
performance_schema没开对应消费者(比如events_statements_history_long),或者表刚 ANALYZE 过但还没被查过,它就直接报“未使用”
sys.schema_unused_indexes 查出来就删?危险
直接删它列出的索引,可能引发以下问题:
- 唯一索引被删 → 后续
INSERT出现重复值不报错(InnoDB 会静默跳过约束检查?不,会报错,但业务可能没兜住) - 外键索引被删 →
UPDATE/DELETE父表时锁表时间暴增,甚至卡死 - 联合索引中部分字段被其他查询依赖(比如只查
a列的 WHERE)→ 删了就变全表扫描 -
SHOW CREATE TABLE里看到UNIQUE KEY或PRIMARY KEY,但sys.schema_unused_indexes把它列进去了?别信,系统主键/唯一键不可能“未使用”
比 sys.schema_unused_indexes 更准的查法
绕过 sys 视图,直查底层表并加业务语义过滤:
SELECT
OBJECT_NAME AS table_name,
INDEX_NAME AS index_name,
COUNT_FETCH,
COUNT_INSERT,
COUNT_UPDATE,
COUNT_DELETE
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA = 'your_db'
AND INDEX_NAME NOT IN ('PRIMARY') -- 排除主键
AND COUNT_FETCH = 0
AND COUNT_INSERT + COUNT_UPDATE + COUNT_DELETE > 0 -- 至少写过
ORDER BY COUNT_INSERT DESC;
重点关注那些 COUNT_FETCH = 0 但 COUNT_INSERT > 0 的索引——它们大概率是冗余的唯一约束或低效的二级索引。
真正要盯的不是“有没有用”,而是“值不值得留”
一个索引是否该删,得看三件事:
- 它是否被任何
EXPLAIN中的key字段引用过(翻慢日志 +pt-index-usage比 sys 视图靠谱) - 它是否让
INSERT/UPDATE变慢(查information_schema.INNODB_METRICS里的dml_inserts延迟趋势) - 它是否和另一个索引前缀重叠(比如已有
INDEX(a,b),又建了INDEX(a))
别光盯着 sys.schema_unused_indexes 输出的那几行,它连“这个索引是不是为 ORDER BY 服务的覆盖索引”都判断不了。











