必须先确认performance_schema=ON并手动启用setup_consumers和setup_instruments中关键项,尤其是wait/io/table/sql/handler;COUNT_FETCH才是索引真实读取次数,需过滤系统库并按其降序排查冗余或低效索引。

不能直接“实时”看到每秒索引调用次数,但能以秒级粒度观察最近几分钟内哪个索引被真正读取了多少次——前提是 performance_schema 已正确启用并打开关键采集器。
确认 performance_schema 和相关采集器是否真在干活
很多人查 table_io_waits_summary_by_index_usage 得到空结果,第一反应是“功能坏了”,其实只是 MySQL 根本没被要求记。必须分两步验证:
- 运行
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 = 'wait/io/table/sql/handler';注意: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是单次优化器的“理论路径”,不等于实际执行;如果 SQL 被 Query Cache 缓存、或客户端缓存了结果,就根本不会走到引擎层,自然不触发索引 I/O - 该统计有轻微延迟(通常秒级),刚执行完立刻查可能还没刷进表里,等 2–3 秒再查更稳
-
COUNT_FETCH是累计值,实例重启后清零;长期观察必须定期快照并做差值,不能只看绝对值
别把 COUNT_STAR 当成“热度”,它只记录“是否被选中过”
MySQL 8.0 没有提供“索引使用热度”的系统变量或内置计数器。COUNT_STAR > 0 只表示“这个索引曾被某条语句选中作为访问路径”,每次语句执行算 1 次,不是每行扫描都 +1;它不区分等值/范围扫描,也不记录扫描行数。所以:
-
COUNT_FETCH = 0且持续多天 → 基本可判定为冗余索引(尤其非主键/唯一约束) -
COUNT_INSERT高但COUNT_FETCH极低 → 这个索引只拖慢写入,几乎不加速查询,性价比极差 - 想判断“某条 SQL 是否真高效用了索引”,必须回退到
EXPLAIN看type、key_len、rows,再结合Handler_read_key/Handler_read_rnd_next状态变量交叉验证
真正麻烦的不是查不到数据,而是忘了:这个表不支持 PERSIST 设置,所有 UPDATE 都只在当前会话有效,重启即失效;生产环境若要长期监控,得把采集开关配置固化进 my.cnf 并纳入部署流程。


















