使用sys.schema_unused_indexes排查废弃索引时需谨慎:该视图仅反映performance_schema采集周期内未被查询选中的索引,重启或采集时间过短易产生假阴性。需确认events_statements_history_long启用且数据非零,并保证业务高峰期持续采集至少24小时。还应交叉验证table_io_waits_summar
如何利用MySQL sys库排查从未使用的废弃索引?
时间:2026-07-21
浏览:604
文章目录
在MySQL索引优化实践中,sys.schema_unused_indexes视图常被用于识别废弃索引,但许多开发者对其存在误读。该视图反映的是performance_schema采集周期内未被查询选中的索引情况,而非绝对意义上的“从未被使用”。若MySQL刚重启或采集时间不足,COUNT_FETCH字段全为零,此时输出的结果多为假阴性,需谨慎对待。
在采取任何索引删除操作之前,务必完成以下三项前置确认:
- 检查
performance_schema.setup_consumers表中events_statements_history_long是否已启用。若该消费者关闭,sys.schema_unused_indexes将无数据可读取。 - 执行
SELECT COUNT(*) FROM events_statements_history_long,返回非零值才说明语句采集功能正常运作。 - 确保在业务高峰期持续采集至少24小时,尤其需覆盖定时任务、报表SQL等低频但关键的业务查询。

直接查询sys.schema_unused_indexes但必须确认数据可信
该视图的判定逻辑并非“索引建了没用就报出”,而是“在performance_schema已采集的查询中一次都未被选中”。MySQL刚重启或采集时间过短时,COUNT_FETCH全部归零,结果将呈现大量假阴性。
实际操作前务必执行以下检查:
SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE 'events_statements_%' AND ENABLED = 'NO'—— 若events_statements_history_long被关闭,sys.schema_unused_indexes将失去数据来源SELECT COUNT(*) FROM performance_schema.events_statements_history_long—— 非零值才表明语句采集已生效- 在业务高峰期持续采集至少24小时,尤其要覆盖定时任务、报表SQL等低频但关键的查询场景
不可仅依赖sys.schema_unused_indexes输出,需交叉验证COUNT_FETCH
该视图仅告知索引“未被使用”,但无法区分是真闲置还是未进入采样窗口。更可靠的判断依据来自底层统计表:
SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, COUNT_FETCH, COUNT_READ FROM performance_schema.table_io_waits_summary_by_index_usage WHERE OBJECT_SCHEMA = 'your_db' AND INDEX_NAME = 'idx_xxx'
重点关注COUNT_FETCH和COUNT_READ是否均为零。若任一字段非零,说明索引确实被使用过;若均为零,需结合EXPLAIN和慢日志进一步判断是否存在漏采。
联合索引中部分列未使用不会被sys.schema_unused_indexes标记
例如存在INDEX(a, b, c),但业务查询中仅出现WHERE a = AND c = ,优化器仍可能选择该索引(key字段显示索引名),但实际仅用到a列,c列因跳过b列而完全失效。
此时需通过EXPLAIN查看执行细节:
- key_len明显小于理论最大值 —— 说明仅使用了前缀列
- Extra字段出现
Using where但未出现Using index—— 表明未走覆盖扫描,大概率发生回表且部分列未参与过滤 - 对于ORDER BY场景,即使WHERE子句未使用全部列,只要排序能被索引覆盖,索引仍可能被选中,不能仅凭key字段存在就判定索引有效
删除索引前必须人工核验三类隐性依赖
自动化脚本输出的“可删除列表”仅为起点,真正危险的是那些不显山露水的隐性依赖:
SELECT COUNT(*) FROM t WHERE ...类聚合查询常走索引快速计数,但不会在table_io_waits_summary_by_index_usage中高频出现- CARDINALITY极低的索引(如status列仅3个值)即使COUNT_FETCH为零也本就不该存在,删除它不解决问题,需重构设计
- ORM框架隐式调用:Django的
.exists()、Laravel的doesntExist()会生成SELECT 1 FROM t WHERE ...,这类语句轻量但关键,容易被忽略
索引是否真正废弃,不取决于它是否出现在执行计划中,而取决于删除后是否会导致某条SQL从ref降级为ALL。这一判断无法全自动化,必须由人工盯住慢日志和核心路径的EXPLAIN结果来最终确认。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。