sys.schema_unused_indexes不可直接采信,因其仅统计performance_schema启用后触发i/o的索引访问,忽略order by、force index、外键引用及低频关键sql;且须确保performance_schema全量开启并运行24小时以上才可信。

直接查 sys.schema_unused_indexes 和 sys.schema_redundant_indexes 能快速筛出候选索引,但“从未被访问”不等于“能删”,必须交叉验证。
为什么 sys.schema_unused_indexes 的结果不能直接信
这个视图只反映 performance_schema 启用后、实际触发 I/O 的索引访问记录。它完全忽略:
- ORDER BY 或 GROUP BY 依赖的索引(即使没 WHERE 条件)
- 应用层硬编码的 FORCE INDEX
- 外键约束隐式引用的索引
- 低频但关键的定时任务 SQL(比如月结报表)
更关键的是:如果 performance_schema 没开全或刚启用,COUNT_READ = 0 就是假阴性。
查之前必须激活 performance_schema 的三项配置
否则 sys.schema_unused_indexes 返回全是空或零值,毫无参考价值:
- 确认 performance_schema 已开启:SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema'; 必须返回 ON
- 启用等待事件采集:UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_waits_current', 'events_statements_history_long');
- 启用表级 I/O 仪器:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'wait/io/table/%';
改完至少等 24 小时,覆盖完整业务周期,再查才可信。
如何用 sys.schema_redundant_indexes 判定真冗余
这个视图纯靠 DDL 结构分析,不依赖运行时数据,相对可靠,但仍有边界:
- 它能准确标出 INDEX(a) 和 INDEX(a, b) 这类前缀完全覆盖关系,并给出 sql_drop_index 语句
- 但它不会告诉你 INDEX(a, b) 和 INDEX(a, c) 是否真冗余——因为字段 b 和 c 不同,功能不可替代
- 注意 dominant_index_non_unique 和 redundant_index_non_unique 值:若后者为 0 且前者为 1,说明冗余索引其实是唯一约束,删前得确认业务是否依赖该唯一性校验
删除前必须跑的三步验证
哪怕两个视图都指向同一个索引,也得人工过一遍:
- 对所有核心查询(含后台任务)执行 EXPLAIN FORMAT=TRADITIONAL,重点看 Extra 字段:如果删掉后出现 Using filesort 或 Using temporary,说明它支撑排序/分组
- 临时禁用索引测试:ALTER TABLE t DROP INDEX idx_name;,再重跑 EXPLAIN,观察 key 字段是否切换到其他索引,或退化为 NULL
- 检查应用代码和 ORM 配置,搜索 FORCE INDEX、USE INDEX 或显式索引名字符串,这类硬编码一炸一个准
最常被跳过的环节是验证外键和唯一约束——sys.schema_redundant_indexes 不会标记它们,但删掉可能让 INSERT 或 UPDATE 报错;而 sys.schema_unused_indexes 也不统计约束检查引发的索引访问。这两类索引得单独查 INFORMATION_SCHEMA.KEY_COLUMN_USAGE 和 INFORMATION_SCHEMA.STATISTICS 确认。











