应先确认 performance_schema 已启用且相关消费者开启,否则 table_io_waits_summary_by_index_usage 返回的 0 值不可信;该视图仅统计启用后实际使用索引的 I/O,不反映隐式失效、排序扫描、外键依赖或 FORCE INDEX 等场景;删索引前须交叉验证外键、代码硬编码索引提示及 EXPLAIN 执行计划,并检查 MDL 锁与手动 ANALYZE TABLE。

直接查 performance_schema.table_io_waits_summary_by_index_usage,但得先确认它开着、数据可信——否则查出来全是 0,你真删了就出事。
为什么 table_io_waits_summary_by_index_usage 可能返回假空值
这个视图默认不收集索引使用统计,哪怕 MySQL 8.0+ 也得手动开:
-
SET GLOBAL performance_schema = ON;(如果没开) - 必须启用对应消费者:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_waits_current';,还要开table_io_waits_summary_by_index_usage对应的 consumer(在setup_instruments和setup_consumers里都得对上) - 该视图只统计开启后新发生的 I/O 操作,重启或长期未启用,历史为 0 不代表“没用过”
- 某些操作(如隐式类型转换、
LIKE '%xxx')会让 MySQL 跳过索引,但不会记入该视图的 COUNT_READ —— 它只记录“实际用了索引去读”,不记录“本该用但没用”
COUNT_STAR = 0 AND COUNT_READ = 0 是必要但不充分条件
满足这两个为 0,只是说明这个索引近期没被任何 SELECT/INSERT/UPDATE/DELETE 实际驱动过 I/O。但它仍可能被以下场景依赖:
-
ORDER BY col ASC LIMIT 10走(col)索引避免Using filesort,但不触发 COUNT_READ(因为没 WHERE,纯排序扫描) - 外键约束索引:删掉会导致
ALTER TABLE ... ADD FOREIGN KEY失败,或已有外键失效 - 应用层写了
FORCE INDEX (idx_name),哪怕查询本身不依赖它,MySQL 也会强制走——这种索引在视图里照样是 0 - 慢查询日志里出现
Extra: Using index且明确带该索引名,但该 SQL 执行频次低,还没刷进统计窗口
删之前必须交叉验证的三件事
光看一个视图就动手,等于蒙眼拆保险丝。
- 查外键依赖:
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;—— 如果有结果,别碰这个索引 - 搜代码和代理日志:
grep -r "FORCE INDEX\|USE INDEX" ./src/或查 SQL 网关(如 ProxySQL、ShardingSphere)日志,确认有没有硬编码索引提示 - 跑一次覆盖性 EXPLAIN:
EXPLAIN FORMAT=JSON SELECT * FROM tbl_name ORDER BY indexed_col LIMIT 1;和EXPLAIN FORMAT=JSON SELECT * FROM tbl_name WHERE non_indexed_col = ? ORDER BY indexed_col;,看是否退化成filesort或全表扫描
ALTER TABLE ... DROP INDEX 卡住不动?先看 MDL 锁
不是语法错,是锁住了。常见于高并发写入表时:
- 执行
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'tbl_name' AND LOCK_STATUS = 'PENDING'; - 如果看到大量
MDL_SHARED_WRITE或MDL_EXCLUSIVE等待,说明有长事务(比如未提交的UPDATE)占着表 - 别直接 KILL,先
SELECT trx_id, trx_started, trx_state FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;找出超 1 分钟的事务,再评估是否可中断 - 真正安全的做法:在业务低峰期,用
pt-kill配合--busy-time自动清理长时间运行事务
最常被忽略的点:删除索引后,ANALYZE TABLE 不会自动触发,优化器仍可能基于过期的统计信息做错误判断。删完立刻手动执行一次,再观察后续慢查询日志里是否出现原本不该有的 Using where; Using filesort。











