清理无用索引是 MySQL 性能优化中见效快、风险低的关键操作。很多业务上线后长期未梳理索引,导致写入变慢、磁盘占用高、执行计划误判。核心思路是:先识别真正未被使用的索引,再结合查询模式和表变更频率谨慎删除。
识别无用索引的可靠方法
不能只看“没被 SELECT 用过”,还要看是否参与了 JOIN、ORDER BY、GROUP BY 或外键约束。推荐组合使用以下方式:
启用 performance_schema 并开启相关消费者(如events_statements_history_long、
table_io_waits_summary_by_index_usage),运行一段时间后查
performance_schema.table_io_waits_summary_by_index_usage,
COUNT_STAR = 0且
INDEX_NAME IS NOT NULL的索引大概率未被使用 用 pt-index-usage(Percona Toolkit)分析慢日志或通用日志,它会反向推导哪些索引在实际查询中从未命中 检查 information_schema.STATISTICS 中重复索引(如已有
(a,b),又建了
(a))和前缀冗余索引(如
(a(10))和
(a(20))共存)
删除前必须验证的三件事
直接 DROP INDEX 可能引发线上故障,务必确认:
该索引是否被外键引用:查information_schema.KEY_COLUMN_USAGE,
CONSTRAINT_NAME非空则不能删 是否有应用层显式指定索引提示(USE INDEX / FORCE INDEX):grep 代码库或代理层 SQL 日志,避免删掉被强制依赖的索引 是否承担了唯一性约束或主键功能:通过
SHOW CREATE TABLE确认索引类型,
UNIQUE KEY或
PRIMARY KEY不属于“无用”范畴
高频易误删的“伪无用”索引
有些索引看似冷门,实则关键:
仅用于 ORDER BY 或 GROUP BY 的覆盖索引:即使没出现在 WHERE 条件里,也能避免文件排序(Using filesort) 低频但高优先级的后台任务索引:如凌晨跑的报表、对账任务,不在白天流量中体现,但删除会导致超时失败 为未来字段预留的复合索引前缀:例如现有查询只用(a),但索引建的是
(a,b,c),后续加了 b/c 过滤条件就能复用
安全清理的操作节奏
建议分阶段推进,最小化影响:
先在从库上执行ALTER TABLE ... DROP INDEX,观察 1–2 天复制延迟与慢查询有无突增 用
EXPLAIN FORMAT=TRADITIONAL对核心接口 SQL 重做执行计划,确认删索引后没退化为全表扫描 生产环境删索引避开业务高峰,单表每次只删 1 个,间隔至少 30 分钟;大表建议用 pt-online-schema-change 在线处理
