必须手动启用performance_schema采集项,否则table_io_waits_summary_by_index_usage永远为空;需确认performance_schema=on,并开启events_waits_history_long和wait/io/table/sql/handler等instrument,count_fetch才是索引真实读取次数的核心指标。

必须手动启用采集项,否则 table_io_waits_summary_by_index_usage 永远为空——这不是 MySQL 没记录,而是你没告诉它要记。
确认 performance_schema 已启用且采集器就位
很多人查 table_io_waits_summary_by_index_usage 得到空结果,第一反应是“功能坏了”,其实只是没开采集开关。先验证基础状态:
- 运行
SHOW VARIABLES LIKE 'performance_schema';,值必须为ON;若为OFF,需在my.cnf中设performance_schema=ON并重启 - 即使已开启,
setup_consumers和setup_instruments默认多数关闭,必须显式打开:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_statements_history_long', 'events_waits_history_long');
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'wait/io/table/%';
注意:wait/io/table/sql/handler 是真正触发索引级统计的底层 instrument,只开 statements_digest 不管用。
查哪个索引被用了多少次:过滤系统库 + 关注 COUNT_FETCH
COUNT_FETCH 是核心指标,代表该索引被用于读取(SELECT/WHERE)的实际次数。但直接查会混入大量内部操作噪音:
- 必须排除系统库干扰:
WHERE OBJECT_SCHEMA NOT IN ('mysql','information_schema','performance_schema') - 聚焦业务库时,加
AND OBJECT_SCHEMA = 'your_db_name' - 按
COUNT_FETCH DESC排序,一眼识别高频/零频索引
示例语句:
SELECT OBJECT_SCHEMA AS db, OBJECT_NAME AS table_name, INDEX_NAME, COUNT_FETCH, COUNT_INSERT
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE OBJECT_SCHEMA NOT IN ('mysql','information_schema','performance_schema')
AND OBJECT_SCHEMA = 'shop'
ORDER BY COUNT_FETCH DESC;
为什么 EXPLAIN 显示走索引,但 COUNT_FETCH 还是 0?
这是时间尺度和执行路径差异导致的常见误判:
-
EXPLAIN是单次优化器的“理论路径”,而COUNT_FETCH是运行时引擎层的真实调用计数 - 刚执行完查询,统计可能有秒级延迟,稍等再查
- SQL 被 Query Cache 或客户端缓存命中,根本没进引擎层,自然不触发索引 I/O
- 某些
SELECT *全表扫描会遍历聚簇索引,但COUNT_FETCH记的是“通过该二级索引查找”的次数,不是所有索引访问都算
所以不能单看 EXPLAIN 就断定索引有效——得看 COUNT_FETCH 是否在真实业务流量中稳定非零。
删索引前最容易忽略的三件事
把 COUNT_FETCH = 0 当成删除依据很危险,因为:
- 刚重启 MySQL 后所有计数归零,
sys.schema_unused_indexes会把全部索引标为“未使用” - 低频但关键的定时任务(如每日报表)可能没覆盖到采样窗口,结果里看不到但它真在用
- 唯一约束、主键、外键依赖的索引不会出现在
sys.schema_unused_indexes,但table_io_waits_summary_by_index_usage里仍会显示COUNT_FETCH—— 删了直接报错
真正安全的做法:先查 COUNT_FETCH,再交叉验证 SHOW CREATE TABLE 看是否承载约束,最后在业务低峰期用 EXPLAIN FORMAT=TREE 重放可疑 SQL,确认执行计划里是否真出现 using index。











