table_io_waits_summary_by_index_usage表用于确认索引是否被实际使用,COUNT_READ、COUNT_FETCH等为0可提示长期未用,但需结合EXPLAIN、外键、唯一约束及业务场景综合判断,不可直接删除。

查 performance_schema.table_io_waits_summary_by_index_usage 确认索引真没被用过
MySQL 8.0+ 才有这个视图,它记录的是运行时索引被实际读写的次数,比 information_schema.STATISTICS 的基数估算靠谱得多。但默认可能关着,得先检查:
-
SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema';确保是ON UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_waits_current';UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'wait/io/table/%';
然后跑这条语句找长期没动静的索引:
SELECT OBJECT_SCHEMA AS db, OBJECT_NAME AS table_name, INDEX_NAME AS index_name FROM performance_schema.table_io_waits_summary_by_index_usage WHERE INDEX_NAME IS NOT NULL AND COUNT_READ = 0 AND COUNT_FETCH = 0 AND COUNT_INSERT = 0 AND COUNT_UPDATE = 0 AND COUNT_DELETE = 0;
注意:主键(PRIMARY)和唯一约束索引(UNIQUE KEY)不能只看这个结果就删——它们可能不参与查询,但承担数据完整性校验。
用 EXPLAIN 验证所有高频 SQL 是否真绕过了该索引
哪怕 COUNT_READ = 0,也不能直接下结论。某些查询会“绕过”索引但实际依赖它,比如:
-
ORDER BY created_at LIMIT 10—— 没WHERE,但靠索引避免Using filesort -
SELECT id, name FROM users WHERE status = 1—— 如果status基数极低,优化器可能放弃走索引,但不代表它没价值 - 带
OR、IN或函数包裹列(如WHERE DATE(created_at) = '2025-01-01')的语句,容易让EXPLAIN显示没走索引,但删了之后反而退化
务必对核心接口的 SQL 跑一遍 EXPLAIN FORMAT=TRADITIONAL,重点盯 key 字段是否出现目标索引名;别漏掉 JOIN 条件、GROUP BY、覆盖查询(Using index)场景。
删索引前必须查外键、唯一约束和联合索引覆盖关系
误删一个被外键引用或承担唯一性保障的索引,会直接报错或破坏数据一致性。执行前必查:
- 是否被外键引用:
SELECT CONSTRAINT_NAME, COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'db_name' AND TABLE_NAME = 'tbl_name' AND CONSTRAINT_NAME != 'PRIMARY' AND REFERENCED_TABLE_NAME IS NOT NULL; - 是否是唯一约束:
SHOW CREATE TABLE tbl_name里看有没有UNIQUE KEY或PRIMARY KEY对应那个索引名 - 是否是冗余前缀索引:比如已有
idx_a_b_c (a,b,c),又单独建了idx_a (a),后者大概率可删;但如果业务里存在大量WHERE a = ? ORDER BY b查询,删掉idx_a可能导致排序失效
联合索引删减要格外小心——删掉前缀列索引(如 (a))不影响 (a,b,c) 的使用,但反过来不行。
千万级表删索引必须用在线方式,避开高峰期
直接 ALTER TABLE tbl DROP INDEX idx_name 在大表上会触发全表拷贝或长时间元数据锁(MDL),高并发写入时极易卡住整个表。稳妥做法是:
- 确认 MySQL 版本支持
ALGORITHM=INPLACE, LOCK=NONE(5.6+ 多数 DDL 支持,但删索引基本都行) - 生产环境选凌晨或流量低谷,单表每次只删 1 个,间隔至少 30 分钟
- 超大表(>5000 万行)建议用
pt-online-schema-change,它通过影子表+触发器实现无锁变更 - 删完立刻查
SHOW PROCESSLIST和performance_schema.metadata_locks,确认没残留阻塞事务
真正容易被忽略的是:索引删除后空间不会立即归还磁盘,ibdata1 或独立表空间里的碎片仍存在,后续需 OPTIMIZE TABLE 或 ALTER TABLE ... ENGINE=InnoDB 才能释放——但这一步本身又重,得权衡。


















