table_io_waits_summary_by_index_usage表需启用wait/io/table/sql/handler instrument并授予权限方可使用;count_fetch是索引读吞吐量核心指标,反映索引读取频次,为冗余索引识别与性能分析关键依据。

确认 table_io_waits_summary_by_index_usage 表是否可用
这个表是监控索引 IO 吞吐量的直接来源,但它不是默认就“能用”的。即使 performance_schema=ON,仍需满足两个前提:
-
wait/io/table/sql/handler这个 instrument 必须启用,否则不采集索引级 I/O 事件 - 用户账号要有
SELECT权限访问performance_schema库(云数据库如 RDS 基础版常禁用该库)
执行这条语句验证:
SELECT NAME, ENABLED, TIMED FROM performance_schema.setup_instruments WHERE NAME = 'wait/io/table/sql/handler';
如果 ENABLED 是 NO,运行:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'wait/io/table/sql/handler';
注意:该修改在实例重启后失效;若需持久化,得在 my.cnf 中加 performance_schema_instrument='wait/io/table/sql/handler=on'。
COUNT_FETCH 就是索引读吞吐量的核心指标
很多人误以为要算字节数或延迟才叫“吞吐量”,其实对索引来说,COUNT_FETCH 直接反映该索引被用于数据读取的频次——它就是最实用的吞吐量代理指标。每次通过该索引定位并读取一行(或一批行),COUNT_FETCH 就 +1。
查某库某表的索引读频次:
SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, COUNT_FETCH AS reads FROM performance_schema.table_io_waits_summary_by_index_usage WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table' AND INDEX_NAME IS NOT NULL ORDER BY COUNT_FETCH DESC;
关键点:
-
COUNT_FETCH = 0的非主键、非唯一约束索引,大概率是冗余的,但删除前必须确认没有隐式查询(如ORDER BY ... LIMIT场景可能走索引但不显式WHERE) -
PRIMARY索引的COUNT_FETCH高,不一定健康——可能说明大量主键扫描,而非高效范围查找 - 该值是自上次
TRUNCATE table_io_waits_summary_by_index_usage或实例启动以来的累计值,不能直接换算成 QPS
别把 COUNT_INSERT/UPDATE/DELETE 当作写吞吐量看
这三个字段名字有误导性:COUNT_INSERT 不代表“插入了多少行”,而是“有多少次 INSERT 操作使用了该索引”——哪怕一条 INSERT ... SELECT 批量插入万行,也只计为 1 次。
所以它们更适合判断索引是否参与 DML 路径,而不是衡量写压力。真正反映写吞吐影响的是:
-
table_io_waits_summary_by_table中的COUNT_WRITE(整张表的写次数) - 结合
INFORMATION_SCHEMA.INNODB_METRICS查dml_inserts、dml_updates等全局计数器 - 观察
SHOW ENGINE INNODB STATUS里的pending normal aio reads/writes是否持续堆积
换句话说:索引的写 IO 吞吐量无法从单个索引维度精确剥离,必须结合表级和引擎级指标交叉判断。
长期监控时,TRUNCATE 表比依赖重启更可控
很多人等实例重启来“清空统计”,但生产环境重启代价大,且会丢失其他正在收集的性能上下文(比如语句摘要、锁等待历史)。更稳妥的做法是定期 TRUNCATE 目标汇总表:
TRUNCATE TABLE performance_schema.table_io_waits_summary_by_index_usage;
这会把所有 COUNT_* 字段归零,但保留表结构和权限,适合做周粒度或变更发布前后的对比基线。
注意两点:
- 执行需
ROOT或PROCESS权限,普通应用账号通常无权操作 -
TRUNCATE是立即生效的 DDL,期间该表不可读(极短暂),建议选低峰期执行
真正容易被忽略的是:这个表的统计不包含覆盖索引(Covering Index)场景下的二级索引读——当 SELECT 列全部命中索引时,InnoDB 可能跳过聚簇索引查找,此时 COUNT_FETCH 只增二级索引,不增 PRIMARY,但你得知道这是优化成功,不是索引没用。











