能删但不能直接删,须先确认未使用(查sys.schema_unused_indexes并确保performance_schema启用)、再识别冗余(如index(a,b)存在时index(a)通常冗余)、最后执行三步验证(外键依赖、主键保护、测试环境流量重放),删除后需盯盘并更新统计信息。

能删,但不能直接删——必须先确认是否真“不再使用”,再判断是否“冗余”,最后分步验证删除。盲目执行 DROP INDEX 是线上事故高发动作。
查哪些索引长期没被优化器选中
MySQL 5.7 的 sys.schema_unused_indexes 是最直接线索,但它依赖 performance_schema 正确启用:
- 先确认采集开关已打开:
SELECT * FROM performance_schema.setup_consumers WHERE NAME IN ('events_statements_history_long', 'table_io_waits_summary_by_index_usage');两行都需为ENABLED - 再查真正零读取的索引:
SELECT object_schema, object_name, index_name FROM sys.schema_unused_indexes WHERE object_schema NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema'); - 注意:该视图只统计「从未被使用」的索引,不包括每月跑一次的报表索引或低频但关键的 JOIN 索引——别光看这一个结果就动手
识别前缀重叠型冗余索引(如 INDEX(a) 和 INDEX(a, b))
冗余不是看名字像,而是看功能是否被完全覆盖。已有 INDEX(user_id, status) 时,INDEX(user_id) 就是冗余的,因为前者能支撑所有仅用 user_id 做等值查询的场景。
- 用这个语句查字段顺序:
SELECT table_name, index_name, seq_in_index, column_name FROM information_schema.statistics WHERE table_schema = 'your_db' ORDER BY table_name, index_name, seq_in_index; - 对每个表,按
index_name分组,逐个比对列序列:如果索引 A 的列是 [a,b,c],索引 B 的列是 [a,b],且 B 没有额外排序/覆盖需求,则 B 冗余 - 特别警惕隐式前缀匹配:
INDEX(a, b)存在时,单独建的INDEX(a)通常无存在必要;但反过来,INDEX(a)存在而你新加了INDEX(a, b, c),旧索引未必冗余——要看业务 SQL 是否仍依赖单列a的快速定位
删之前必须绕不开的三件事
跳过任意一项,都可能在高峰期引发慢查询雪崩。
- 检查外键依赖:
SELECT COUNT(*) FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'your_db' AND COLUMN_NAME = 'xxx' AND CONSTRAINT_NAME LIKE 'fk_%';如果该索引字段被外键引用,DROP INDEX会失败,必须先删外键约束 - 确认主键未被误删:
SHOW INDEX FROM your_table;中Key_name = 'PRIMARY'的那一行不能用DROP INDEX,只能用ALTER TABLE ... DROP PRIMARY KEY,且 InnoDB 表删主键后若无其他非空唯一索引,会报错 - 在测试环境重放真实流量:用 pt-query-digest 解析最近 24 小时慢日志,挑出高频 SQL,
EXPLAIN对比删索引前后执行计划是否变化;尤其关注type是否从ref退化成ALL,rows是否暴涨
执行删除与后续盯盘要点
生产环境删索引不是命令一敲就完事,关键是控制影响面和可回滚性。
- 一次只删一个索引,用
ALTER TABLE your_table DROP INDEX idx_name;(比DROP INDEX更统一,兼容性更好) - 大表操作务必加
pt-online-schema-change或选在低峰期,避免长事务阻塞 DML - 删完立刻盯三项指标:慢查询数量是否上升、
Handler_read_next是否突增(说明走了全表扫描)、写入延迟(innodb_row_lock_time_avg)是否波动 - 最容易被忽略的是统计信息老化:删完索引后运行
ANALYZE TABLE your_table;,否则优化器可能继续基于过时分布做错误选择











