必须先确认performance_schema已启用且table_io_waits_summary_by_index_usage消费者开启,否则sys.schema_unused_indexes查询结果为空;该视图仅适用于mysql 5.7+,需排除系统库及primary、unique等约束索引,并结合schema_redundant_indexes识别逻辑冗余索引。

查 sys.schema_unused_indexes 前必须确认 performance_schema 已启用且相关消费者开启
这个视图底层依赖 performance_schema.table_io_waits_summary_by_index_usage,如果没开,查出来永远是空的。别急着执行 SELECT,先验证:
-
SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema';—— 必须为ON -
SELECT * FROM performance_schema.setup_consumers WHERE NAME IN ('events_statements_history_long', 'table_io_waits_summary_by_index_usage');—— 两行的ENABLED列都得是YES - 若未启用,需在 my.cnf 中加
performance_schema=ON并重启;动态开启只对部分变量有效,table_io_waits_summary_by_index_usage必须重启才生效
直接查 sys.schema_unused_indexes 但要排除系统库和约束索引
该视图默认包含所有 schema,但 mysql、sys、information_schema、performance_schema 里的索引不能动,PRIMARY 和 UNIQUE 约束隐式创建的索引也不能删(哪怕 count_star=0):
- 安全查询写法:
SELECT object_schema, object_name, index_name FROM sys.schema_unused_indexes WHERE object_schema NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema') AND index_name != 'PRIMARY' AND index_name NOT IN (SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE CONSTRAINT_SCHEMA = object_schema AND TABLE_NAME = object_name AND CONSTRAINT_TYPE IN ('UNIQUE', 'FOREIGN KEY')); - 注意:
sys.schema_unused_indexes在 MySQL 5.7+ 才原生支持;5.6 及更早版本需手动拼performance_schema.table_io_waits_summary_by_index_usage查询,且无法用视图名
冗余索引不能只看「未使用」,还得查 sys.schema_redundant_indexes
一个索引可能被高频使用,但仍是冗余的——比如已有 INDEX(a,b,c),又建了 INDEX(a,b),后者逻辑上完全被覆盖,删掉不会影响查询,反而能加速写入:
- 查冗余索引:
SELECT table_schema, table_name, redundant_index_name, redundant_index_columns, dominant_index_name, dominant_index_columns FROM sys.schema_redundant_indexes; -
redundant_index_columns是可安全删除的索引列组合,dominant_index_columns是保留的那个更强索引 - 特别注意外键:MySQL 会为 FOREIGN KEY 自动创建索引,
DROP INDEX时若该索引正被外键引用,会报错ERROR 1553 (HY000): Cannot drop index 'xxx': needed in a foreign key constraint,删前务必用SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'db' AND INDEX_NAME = 'idx_xxx';配合information_schema.KEY_COLUMN_USAGE检查是否关联外键
删索引不是元数据操作那么简单,大表上仍可能锁表或拖慢主从
MySQL 5.7+ 的 ALTER TABLE ... DROP INDEX 默认是 inplace,但某些场景下仍会触发 table rebuild(如唯一索引 + 有全文索引),尤其在低版本或特定存储引擎下:
- 操作前停从库:
STOP SLAVE;,删完观察SHOW SLAVE STATUS\G的Seconds_Behind_Master是否归零再START SLAVE; - 避免高峰期执行,大表建议搭配
pt-online-schema-change(注意:它不支持直接删索引,需用--alter "DROP INDEX xxx"参数) - 删完立刻验证:
EXPLAIN SELECT ...关键查询,确认执行计划没退化;同时盯 15 分钟内慢查询日志,log_queries_not_using_indexes = ON要开着
真正容易被忽略的是:索引“未使用”可能是时间窗口太短(比如只开了 24 小时监控),也可能是应用用了 ORM 自动生成 SQL,绕过了你认为该走的索引路径。别单凭一次查询就动手,至少观察一个完整业务周期。











